Skip to content

Commit

Permalink
update for tree & bush changes in SDV 1.6
Browse files Browse the repository at this point in the history
  • Loading branch information
Pathoschild committed Dec 1, 2023
1 parent 37871f9 commit 0725dfc
Show file tree
Hide file tree
Showing 30 changed files with 209 additions and 145 deletions.
2 changes: 1 addition & 1 deletion Automate/Framework/AutomationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public AutomationFactory(Func<ModConfig> config, IMonitor monitor, IReflectionHe
case FruitTree fruitTree:
return new FruitTreeMachine(fruitTree, location, tile);

case Tree tree when TreeMachine.CanAutomate(tree) && tree.growthStage.Value >= Tree.treeStage: // avoid accidental machine links due to seeds spreading automatically
case Tree tree when TreeMachine.CanAutomate(tree, location) && tree.growthStage.Value >= Tree.treeStage: // avoid accidental machine links due to seeds spreading automatically
return new TreeMachine(tree, location, tile);
}

Expand Down
2 changes: 1 addition & 1 deletion Automate/Framework/Machines/Objects/TapperMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private void Reset(Item item)
{
this.Machine.heldObject.Value = null;
this.Machine.readyForHarvest.Value = false;
this.Tree.UpdateTapperProduct(tapper_instance: this.Machine, previous_object: item as SObject);
this.Tree.UpdateTapperProduct(tapper: this.Machine, previousOutput: item as SObject);
}
}
}
8 changes: 3 additions & 5 deletions Automate/Framework/Machines/TerrainFeatures/BushMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public BushMachine(Bush bush, GameLocation location, Rectangle tileArea)
: base(bush, location, tileArea)
{
this.IsInSeason = new Cached<bool>(
getCacheKey: () => $"{Game1.GetSeasonForLocation(bush.currentLocation)},{Game1.dayOfMonth},{bush.overrideSeason.Value}",
getCacheKey: () => $"{Game1.GetSeasonForLocation(bush.Location)},{Game1.dayOfMonth},{bush.IsSheltered()}",
fetchNew: this.RecalculateIsInSeason
);
}
Expand Down Expand Up @@ -128,19 +128,17 @@ private bool RecalculateIsInSeason()
{
// get info
Bush bush = this.Machine;
string season = Game1.GetSeasonForLocation(bush.currentLocation);
int day = Game1.dayOfMonth;

// check if in season
if (bush.tileSheetOffset.Value == 1)
return bush.inBloom(season, day);
return bush.inBloom();

// workaround: we want to know if it's in season, not whether it's currently blooming
int prevOffset = bush.tileSheetOffset.Value;
try
{
bush.tileSheetOffset.Value = 1;
return bush.inBloom(season, day);
return bush.inBloom();
}
finally
{
Expand Down
24 changes: 3 additions & 21 deletions Automate/Framework/Machines/TerrainFeatures/FruitTreeMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public override MachineState GetState()
if (this.Machine.growthStage.Value < FruitTree.treeStage)
return MachineState.Disabled;

return this.Machine.fruitsOnTree.Value > 0
return this.Machine.fruit.Count > 0
? MachineState.Done
: MachineState.Processing;
}
Expand All @@ -37,17 +37,10 @@ public override ITrackedStack GetOutput()

// if struck by lightning => coal
if (tree.struckByLightningCountdown.Value > 0)
return new TrackedItem(ItemRegistry.Create(SObject.coalQID, tree.fruitsOnTree.Value), onReduced: this.OnOutputReduced);
return new TrackedItem(ItemRegistry.Create(SObject.coalQID, tree.fruit.Count), onReduced: _ => tree.fruit.Clear());

// else => fruit
int quality = SObject.lowQuality;
if (tree.daysUntilMature.Value <= -112)
quality = SObject.medQuality;
if (tree.daysUntilMature.Value <= -224)
quality = SObject.highQuality;
if (tree.daysUntilMature.Value <= -336)
quality = SObject.bestQuality;
return new TrackedItem(new SObject(tree.indexOfFruit.Value, tree.fruitsOnTree.Value, quality: quality), onReduced: this.OnOutputReduced);
return new TrackedItem(tree.fruit[^1], onReduced: item => tree.fruit.Remove(item));
}

/// <summary>Provide input to the machine.</summary>
Expand All @@ -57,16 +50,5 @@ public override bool SetInput(IStorage input)
{
return false; // no input
}


/*********
** Private methods
*********/
/// <summary>Reset the machine so it's ready to accept a new input.</summary>
/// <param name="item">The output item that was taken.</param>
private void OnOutputReduced(Item item)
{
this.Machine.fruitsOnTree.Value = item.Stack;
}
}
}
34 changes: 16 additions & 18 deletions Automate/Framework/Machines/TerrainFeatures/TreeMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Pathoschild.Stardew.Common.Enums;
using Pathoschild.Stardew.Common.Utilities;
using StardewValley;
using StardewValley.GameData.WildTrees;
using StardewValley.Locations;
using StardewValley.TerrainFeatures;
using SObject = StardewValley.Object;
Expand Down Expand Up @@ -59,7 +60,7 @@ public override MachineState GetState()
if (!drops.Any())
{
// seed must be last item dropped, since collecting the seed will reset the stack
string? seedId = TreeMachine.GetSeedForTree(tree);
string? seedId = TreeMachine.GetSeedForTree(tree, this.Location);
if (seedId != null)
drops.Push(seedId);

Expand All @@ -84,9 +85,10 @@ public override bool SetInput(IStorage input)

/// <summary>Get whether a tree can be automated.</summary>
/// <param name="tree">The tree to automate.</param>
public static bool CanAutomate(Tree tree)
/// <param name="location">The location containing the tree.</param>
public static bool CanAutomate(Tree tree, GameLocation location)
{
return TreeMachine.GetSeedForTree(tree) != null;
return TreeMachine.GetSeedForTree(tree, location) != null;
}


Expand Down Expand Up @@ -119,7 +121,7 @@ private bool HasSeed()
private IEnumerable<string> GetRandomExtraDrops()
{
Tree tree = this.Machine;
TreeType type = (TreeType)tree.treeType.Value;
string type = tree.treeType.Value;

// golden coconut
if ((type == TreeType.Palm || type == TreeType.Palm2) && this.Location is IslandLocation && new Random((int)Game1.uniqueIDForThisGame + (int)Game1.stats.DaysPlayed + this.TileArea.X * 13 + this.TileArea.Y * 54).NextDouble() < 0.1)
Expand All @@ -133,21 +135,17 @@ private IEnumerable<string> GetRandomExtraDrops()

/// <summary>Get the seed ID dropped by a tree, regardless of whether it currently has a seed.</summary>
/// <param name="tree">The tree instance.</param>
private static string? GetSeedForTree(Tree tree)
/// <param name="location">The location containing the tree.</param>
/// <remarks>Derived from <see cref="Tree.shake"/>.</remarks>
private static string? GetSeedForTree(Tree tree, GameLocation location)
{
TreeType type = (TreeType)tree.treeType.Value;
return type switch
{
TreeType.Oak => "309", // acorn
TreeType.Maple => Game1.GetSeasonForLocation(tree.currentLocation) == "fall" && Game1.dayOfMonth >= 14
? "408" // hazelnut
: "310", // maple seed
TreeType.Pine => "311", // pine code
TreeType.Palm => "88", // coconut
TreeType.Palm2 => "88", // coconut
TreeType.Mahogany => "292", // mahogany seed
_ => null
};
WildTreeData? data = tree.GetData();

string? seed = data?.SeedItemId;
if (Game1.GetSeasonForLocation(location) == "fall" && seed == "309"/*acorn*/ && Game1.dayOfMonth >= 14)
seed = "408";/*hazelnut*/

return seed;
}
}
}
29 changes: 21 additions & 8 deletions Common/Enums/TreeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@
namespace Pathoschild.Stardew.Common.Enums
{
/// <summary>Indicates a tree type.</summary>
internal enum TreeType
internal static class TreeType
{
BigMushroom = Tree.mushroomTree,
Mahogany = Tree.mahoganyTree,
Maple = Tree.leafyTree,
Oak = Tree.bushyTree,
Palm = Tree.palmTree,
Palm2 = Tree.palmTree2,
Pine = Tree.pineTree
/// <summary>The tree type ID for a big mushroom tree.</summary>
public static string BigMushroom { get; } = Tree.mushroomTree.ToString();

/// <summary>The tree type ID for a mahogany tree.</summary>
public static string Mahogany { get; } = Tree.mahoganyTree.ToString();

/// <summary>The tree type ID for a maple tree.</summary>
public static string Maple { get; } = Tree.leafyTree.ToString();

/// <summary>The tree type ID for an oak tree.</summary>
public static string Oak { get; } = Tree.bushyTree.ToString();

/// <summary>The tree type ID for a palm tree.</summary>
public static string Palm { get; } = Tree.palmTree.ToString();

/// <summary>The tree type ID for a palm2 tree.</summary>
public static string Palm2 { get; } = Tree.palmTree2.ToString();

/// <summary>The tree type ID for a pine tree.</summary>
public static string Pine { get; } = Tree.pineTree.ToString();
}
}
15 changes: 0 additions & 15 deletions LookupAnything/Framework/Constants/WildTreeGrowthStage.cs

This file was deleted.

1 change: 1 addition & 0 deletions LookupAnything/Framework/I18nExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using StardewModdingAPI;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.GameData.WildTrees;
using StardewValley.Network;

namespace Pathoschild.Stardew.LookupAnything.Framework
Expand Down
12 changes: 3 additions & 9 deletions LookupAnything/Framework/Lookups/TerrainFeatures/BushSubject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ internal class BushSubject : BaseSubject
/// <summary>The underlying target.</summary>
private readonly Bush Target;

/// <summary>The location which contains the bush.</summary>
private readonly GameLocation Location;

/// <summary>Simplifies access to private game code.</summary>
private readonly IReflectionHelper Reflection;

Expand All @@ -34,13 +31,11 @@ internal class BushSubject : BaseSubject
/// <summary>Construct an instance.</summary>
/// <param name="gameHelper">Provides utility methods for interacting with the game code.</param>
/// <param name="bush">The lookup target.</param>
/// <param name="location">The location which contains the bush.</param>
/// <param name="reflection">Simplifies access to private game code.</param>
public BushSubject(GameHelper gameHelper, Bush bush, GameLocation location, IReflectionHelper reflection)
public BushSubject(GameHelper gameHelper, Bush bush, IReflectionHelper reflection)
: base(gameHelper)
{
this.Target = bush;
this.Location = location;
this.Reflection = reflection;

if (this.IsBerryBush(bush))
Expand Down Expand Up @@ -95,9 +90,8 @@ public override IEnumerable<IDebugField> GetDebugFields()

// pinned fields
yield return new GenericDebugField("health", target.health, pinned: true);
yield return new GenericDebugField("is greenhouse bush", this.Stringify(target.greenhouseBush.Value), pinned: true);
yield return new GenericDebugField("is town bush", this.Stringify(target.townBush.Value), pinned: true);
yield return new GenericDebugField("is in bloom", this.Stringify(target.inBloom(Game1.currentSeason, Game1.dayOfMonth)), pinned: true);
yield return new GenericDebugField("is in bloom", this.Stringify(target.inBloom()), pinned: true);

// raw fields
foreach (IDebugField field in this.GetDebugFieldsFrom(target))
Expand Down Expand Up @@ -194,7 +188,7 @@ private SDate GetNextHarvestDate(Bush bush)
if (minDate < tomorrow)
minDate = tomorrow;

if (minDate.Season == "winter" && !bush.greenhouseBush.Value && this.Location.locationContext != GameLocation.LocationContext.Island)
if (minDate.Season == "winter" && !bush.IsSheltered())
return new(22, "spring", minDate.Year + 1);
if (minDate.Day < 22)
return new(22, minDate.Season);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Pathoschild.Stardew.LookupAnything.Framework.Fields;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.GameData.FruitTrees;
using StardewValley.TerrainFeatures;

namespace Pathoschild.Stardew.LookupAnything.Framework.Lookups.TerrainFeatures
Expand All @@ -33,7 +34,7 @@ internal class FruitTreeSubject : BaseSubject
/// <param name="tree">The lookup target.</param>
/// <param name="tile">The tree's tile position.</param>
public FruitTreeSubject(GameHelper gameHelper, FruitTree tree, Vector2 tile)
: base(gameHelper, I18n.FruitTree_Name(fruitName: gameHelper.GetObjectById(tree.indexOfFruit.Value).DisplayName), null, I18n.Type_FruitTree())
: base(gameHelper, I18n.FruitTree_Name(fruitName: FruitTreeSubject.GetDisplayName(tree)), null, I18n.Type_FruitTree())
{
this.Target = tree;
this.Tile = tile;
Expand All @@ -60,7 +61,7 @@ public override IEnumerable<ICustomField> GetData()
yield return new GenericField(label, I18n.FruitTree_NextFruit_StruckByLightning(count: tree.struckByLightningCountdown.Value));
else if (!this.IsInSeason(tree, nextFruit.Season))
yield return new GenericField(label, I18n.FruitTree_NextFruit_OutOfSeason());
else if (tree.fruitsOnTree.Value == FruitTree.maxFruitsOnTrees)
else if (tree.fruit.Count >= FruitTree.maxFruitsOnTrees)
yield return new GenericField(label, I18n.FruitTree_NextFruit_MaxFruit());
else
yield return new GenericField(label, I18n.Generic_Tomorrow());
Expand Down Expand Up @@ -112,11 +113,17 @@ public override IEnumerable<ICustomField> GetData()
}
}

// show season
yield return new GenericField(
I18n.FruitTree_Season(),
I18n.FruitTree_Season_Summary(I18n.GetSeasonName(tree.fruitSeason.Value == "island" ? "summer" : tree.fruitSeason.Value))
);
// show seasons
{
IEnumerable<string>? seasons = tree.GetData()?.Seasons?.Select(I18n.GetSeasonName);
if (seasons != null)
{
yield return new GenericField(
I18n.FruitTree_Season(),
I18n.FruitTree_Season_Summary(string.Join(", ", seasons))
);
}
}
}

/// <summary>Get raw debug data to display for this subject.</summary>
Expand Down Expand Up @@ -149,6 +156,18 @@ public override bool DrawPortrait(SpriteBatch spriteBatch, Vector2 position, Vec
/*********
** Private methods
*********/
/// <summary>Get the display name for a tree, without the 'tree' suffix (like "Banana" for a banana tree).</summary>
/// <param name="tree">The fruit tree.</param>
private static string GetDisplayName(FruitTree tree)
{
FruitTreeData? data = tree.GetData();
string displayName = TokenParser.ParseText(data?.DisplayName);

return string.IsNullOrWhiteSpace(data?.DisplayName)
? "???"
: displayName;
}

/// <summary>Get the fruit quality produced by a tree.</summary>
/// <param name="tree">The fruit tree.</param>
/// <param name="daysPerQuality">The number of days before the tree begins producing a higher quality.</param>
Expand Down Expand Up @@ -195,11 +214,18 @@ private IEnumerable<KeyValuePair<ItemQuality, int>> GetQualitySchedule(FruitTree
/// <remarks>Derived from <see cref="FruitTree.IsInSeasonHere"/> and <see cref="FruitTree.seasonUpdate"/>.</remarks>
private bool IsInSeason(FruitTree tree, string season)
{
if (season == tree.fruitSeason.Value || tree.currentLocation.SeedsIgnoreSeasonsHere())
if (tree.currentLocation.SeedsIgnoreSeasonsHere())
return true;

if (tree.fruitSeason.Value == "island")
return season == "summer" || tree.currentLocation.GetLocationContext() == GameLocation.LocationContext.Island;
List<string>? growSeasons = tree.GetData()?.Seasons;
if (growSeasons != null)
{
foreach (string growSeason in growSeasons)
{
if (string.Equals(growSeason, season, StringComparison.OrdinalIgnoreCase))
return true;
}
}

return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public override Rectangle GetSpritesheetArea()
}

// grown tree
return new Rectangle(this.SourceRect.X + ((12 + (tree.GreenHouseTree ? 1 : Utility.getSeasonNumber(Game1.currentSeason)) * 3) * 16), this.SourceRect.Y, 48, 16 + 64);
return new Rectangle(this.SourceRect.X + ((12 + (tree.IgnoresSeasonsHere() ? 1 : Utility.getSeasonNumber(Game1.currentSeason)) * 3) * 16), this.SourceRect.Y, 48, 16 + 64);
}

/// <summary>Get a rectangle which roughly bounds the visible sprite relative the viewport.</summary>
Expand Down Expand Up @@ -121,8 +121,8 @@ public void GetSpriteSheet(FruitTree target, JsonAssetsIntegration jsonAssets, o
return;

// use vanilla logic
texture = FruitTree.texture;
sourceRect = new Rectangle(x: 0, y: target.treeType.Value * 5 * 16, width: 432, height: 80);
texture = target.texture;
sourceRect = new Rectangle(x: 0, y: target.GetSpriteRowNumber() * 5 * 16, width: 432, height: 80);
}
}
}
Loading

0 comments on commit 0725dfc

Please sign in to comment.