Skip to content

Commit

Permalink
Merge pull request #70 from slothsoft/bugfix/much-fix-so-wow
Browse files Browse the repository at this point in the history
Bugfixes
  • Loading branch information
slothsoft authored Nov 19, 2022
2 parents d01af95 + c141b31 commit 0a72a4d
Show file tree
Hide file tree
Showing 23 changed files with 224 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,24 @@ public void CalculateDaysLeftInGreenhouse(int dayOfMonth, string season, int day
};
Assert.AreEqual(expectedDaysLeft, _classUnderTest.CalculateDaysLeft(fruitTree));
}

[Test]
[TestCase(10,Seasons.Spring, 10, 0, Seasons.Summer, 10)]
[TestCase(10,Seasons.Spring, 18, 0, Seasons.Spring, 18)]
[TestCase(10,Seasons.Fall, 0, 1, Seasons.Fall, 0)]
[TestCase(5,Seasons.Spring, 0, 0, "unsupported season", 1)] // we don't check the season
public void CalculateDaysLeftOnIsland(int dayOfMonth, string season, int daysUntilMature, int fruitsOnTree, string fruitSeason, int expectedDaysLeft) {
Game1.dayOfMonth = dayOfMonth;
Game1.currentSeason = season;

var fruitTree = new FruitTree {
currentLocation = new Farm {
name = { "IslandWest" },
},
daysUntilMature = { daysUntilMature },
fruitsOnTree = { fruitsOnTree },
fruitSeason = { fruitSeason },
};
Assert.AreEqual(expectedDaysLeft, _classUnderTest.CalculateDaysLeft(fruitTree));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Slothsoft.Informant.Implementation.Common;
using Slothsoft.Informant.Implementation.TooltipGenerator;
using StardewTests.Harness;
using StardewValley.Objects;

namespace InformantTest.Implementation.TooltipGenerator;

Expand Down Expand Up @@ -110,4 +111,32 @@ public void CalculateMinutesLeftString(int minutesLeft, string expectedString) {
};
Assert.AreEqual(expectedString, _classUnderTest.CalculateMinutesLeftString(obj));
}

[Test]
[TestCase(1, 56, 0, "Finished")]
public void CalculateMinutesLeftStringForCaskWithMinutesLeft(int minutesLeft, int daysToMature, int quality, string expectedString) {
var obj = new Cask {
minutesUntilReady = { minutesLeft },
daysToMature = { daysToMature },
heldObject = { new SObject {
quality = { quality },
} }
};
Assert.AreEqual(expectedString, _classUnderTest.CalculateMinutesLeftString(obj));
}

[Test]
[TestCase(56, 0, "14 days left for next quality\n56 days left in total")]
[TestCase(56 - 13, 0, "1 day left for next quality\n43 days left in total")]
[TestCase(56 - 13, 1, "15 days left for next quality\n43 days left in total")]
[TestCase(56 - 13, 2, "43 days left for next quality\n43 days left in total")]
public void CalculateMinutesLeftStringForCask(int daysToMature, int quality, string expectedString) {
var obj = new Cask {
daysToMature = { daysToMature },
heldObject = { new SObject {
quality = { quality },
} }
};
Assert.AreEqual(expectedString, _classUnderTest.CalculateMinutesLeftString(obj));
}
}
2 changes: 1 addition & 1 deletion Informant.Test/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"Name": "Informant.Test",
"Author": "Slothsoft",
"Version": "1.3.0",
"Version": "1.3.1",
"Description": "Test Project.",
"UniqueID": "Slothsoft.Informant.Test",
"EntryDll": "Informant.Test.dll",
Expand Down
5 changes: 4 additions & 1 deletion Informant/Api/Decoration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ namespace Slothsoft.Informant.Api;
/// </summary>
/// <param name="Texture">the texture to display.</param>
public record Decoration(Texture2D Texture) {

/// <summary>
/// Optionally displays a little number over the texture.
/// </summary>
public int? Counter { get; init; }
}
15 changes: 15 additions & 0 deletions Informant/Api/Icon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ namespace Slothsoft.Informant.Api;
/// </summary>
/// <param name="Texture">the texture to display.</param>
public record Icon(Texture2D Texture) {
/// <summary>
/// Create an icon for an Stardew Valley <see cref="SObject"/>.
/// </summary>
public static Icon? ForObject(SObject obj, IPosition? position = null, Vector2? iconSize = null) {
return ForParentSheetIndex(obj.ParentSheetIndex, obj.bigCraftable.Value, position, iconSize);
}

/// <summary>
/// Create an icon for a parentSheetIndex and bigCraftable.
/// </summary>
public static Icon? ForParentSheetIndex(int parentSheetIndex, bool bigCraftable, IPosition? position = null, Vector2? iconSize = null) {
position ??= IPosition.TopRight;
iconSize ??= new Vector2(Game1.tileSize, Game1.tileSize);
Expand All @@ -36,8 +42,17 @@ public record Icon(Texture2D Texture) {
};
}

/// <summary>
/// Optionally defines the source rectangle of the texture. Will be the entire <see cref="Texture"/> if not set.
/// </summary>
public Rectangle? SourceRectangle { get; init; }
/// <summary>
/// Optionally defines the position of the icon. Will be <see cref="IPosition.TopLeft"/> if not set.
/// </summary>
public IPosition? Position { get; init; }
/// <summary>
/// Optionally defines the size of the icon. Will be the <see cref="Texture"/>'s size if not set.
/// </summary>
public Vector2? IconSize { get; init; }

internal Rectangle NullSafeSourceRectangle => SourceRectangle ?? new Rectangle(0, 0, Texture.Width, Texture.Height);
Expand Down
3 changes: 3 additions & 0 deletions Informant/Api/Tooltip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
/// </summary>
/// <param name="Text">the multiline text to display.</param>
public record Tooltip(string Text) {
/// <summary>
/// Optionally displays an icon around the tooltip.
/// </summary>
public Icon? Icon { get; init; }
}
9 changes: 9 additions & 0 deletions Informant/Implementation/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,13 @@ public static class BigCraftableIds {
WoodLamppost, IronLamppost, Hmtgf, JunimoKartArcadeSystem, PinkyLemon, Foroguemo, SolidGoldLewis, AutoGrabber, DeluxeScarecrow, Barrel, Crate,
Workbench, MiniJukebox, Telephone, CursedPkArcadeSystem, MiniObelisk, FarmComputer, SewingMachine, AutoPetter, Hopper, Campfire2,
}).ToArray();
}

/// <summary>
/// These constants can be used to compare to <code>Object.ParentSheetIndex</code>.
/// See https://stardewcommunitywiki.com/Modding:Object_data
/// </summary>
public static class ObjectIds {
public const int GingerForageCropId = 2;
public const int Ginger = 829;
}
41 changes: 36 additions & 5 deletions Informant/Implementation/Decorator/ShippingBinDecorator.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using Microsoft.Xna.Framework.Graphics;
using Slothsoft.Informant.Api;

namespace Slothsoft.Informant.Implementation.Decorator;

internal class ShippingBinDecorator : IDecorator<Item> {


private static readonly int[] Ship15Items =
{24, 188, 190, 192, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 300, 304, 398, 400, 433};

private static Texture2D? _shippingBin;

private readonly IModHelper _modHelper;
Expand All @@ -20,13 +24,40 @@ public ShippingBinDecorator(IModHelper modHelper) {

public bool HasDecoration(Item input) {
if (_shippingBin != null && input is SObject obj && !obj.bigCraftable.Value) {
return obj.countsForShippedCollection() &&
(!Game1.player.basicShipped.ContainsKey(obj.ParentSheetIndex) || Game1.player.basicShipped[obj.ParentSheetIndex] == 0);
if (!obj.countsForShippedCollection()) {
// we do not need to ship this item
return false;
}
var alreadyShipped = Game1.player.basicShipped.ContainsKey(input.ParentSheetIndex) ? Game1.player.basicShipped[input.ParentSheetIndex] : 0;

if (!Ship15Items.Contains(input.ParentSheetIndex)) {
// we only need to ship this item once
return alreadyShipped == 0;
}
const int needToBeShipped = 15;
// we need to ship this item 15 times
return alreadyShipped < needToBeShipped;
}
return false;
}

public Decoration Decorate(Item input) {
return new Decoration(_shippingBin!);
return new Decoration(_shippingBin!) {
Counter = CalculateStillNeeded(input),
};
}

public int? CalculateStillNeeded(Item input) {
if (!Ship15Items.Contains(input.ParentSheetIndex)) {
// we don't need to show any number because we don't need to ship 15
return null;
}
var alreadyShipped = Game1.player.basicShipped.ContainsKey(input.ParentSheetIndex) ? Game1.player.basicShipped[input.ParentSheetIndex] : 0;
const int needToBeShipped = 15;
if (alreadyShipped >= needToBeShipped) {
// we don't need to ship anything? why even show the decorator?
return null;
}
return needToBeShipped - alreadyShipped;
}
}
15 changes: 14 additions & 1 deletion Informant/Implementation/ItemDecoratorManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Immutable;
using System.Linq;
using HarmonyLib;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Slothsoft.Informant.Api;
using StardewValley.Menus;
Expand Down Expand Up @@ -78,10 +79,22 @@ private static void DrawToolTip(SpriteBatch b, Item? hoveredItem) {
decoratorsBox.X + indent,
decoratorsBox.Y + indent,
decoratorsHeight - 2 * indent,
decoratorsHeight - 2 * indent);
decoratorsHeight - 2 * indent
);

foreach (var decoration in decorations) {
b.Draw(decoration.Texture, destinationRectangle, null, Color.White);

var counter = decoration.Counter;
if (counter != null) {
const float scale = 0.5f;
// these x and y coordinates are the top left of the right-most number of the counter
var x = destinationRectangle.X + destinationRectangle.Width - NumberSprite.getWidth(counter.Value % 10) + 2;
var y = destinationRectangle.Y + destinationRectangle.Height - NumberSprite.getHeight() + 2;
NumberSprite.draw(counter.Value, b, new Vector2(x, y), Color.White, scale, 1, 1, 0);
}


destinationRectangle.X += destinationRectangle.Width + Game1.pixelZoom;
}
}
Expand Down
8 changes: 7 additions & 1 deletion Informant/Implementation/NewRecipeDisplayable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Slothsoft.Informant.Implementation;

internal class NewRecipeDisplayable : IDisplayable {

private static string DisplayableId => "new-recipe";
private static readonly Rectangle NewSourceRectangle = new(144, 440, 16, 7);

private readonly IModHelper _modHelper;
Expand Down Expand Up @@ -49,7 +50,7 @@ public NewRecipeDisplayable(IModHelper modHelper, string? uniqueId = null) {
);
}

public string Id => "new-recipe";
public string Id => DisplayableId;
public string DisplayName => _modHelper.Translation.Get("NewRecipeDisplayable");
public string Description => _modHelper.Translation.Get("NewRecipeDisplayable.Description");

Expand All @@ -64,6 +65,11 @@ private static void DrawOverlayIfNecessary(ClickableTextureComponent __instance,
// we are on the recipe page, but have no recipe? ignore!
return;
}

var config = InformantMod.Instance?.Config ?? new InformantConfig();
if (!config.DisplayIds.GetValueOrDefault(DisplayableId, true)) {
return; // this "decorator" is deactivated
}

// recipe.timesCrafted is not updated it seems
var timesCrafted = recipe.isCookingRecipe
Expand Down
7 changes: 4 additions & 3 deletions Informant/Implementation/SellPriceDisplayable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
namespace Slothsoft.Informant.Implementation;

internal class SellPriceDisplayable : IDisplayable {

private static string DisplayableId => "sell-price";
private static readonly Rectangle CoinSourceBounds = new(5, 69, 6, 6);

private record MoneyToDisplay(int One, int? Stack);
Expand Down Expand Up @@ -51,7 +53,7 @@ public SellPriceDisplayable(IModHelper modHelper, string? uniqueId = null) {
);
}

public string Id => "sell-price";
public string Id => DisplayableId;
public string DisplayName => _modHelper.Translation.Get("SellPriceDisplayable");
public string Description => _modHelper.Translation.Get("SellPriceDisplayable.Description");

Expand All @@ -61,9 +63,8 @@ public SellPriceDisplayable(IModHelper modHelper, string? uniqueId = null) {
private static void ManipulateMoneyValue(Item? hoveredItem, ref int moneyAmountToShowAtBottom, out MoneyToDisplay? __state) {
__state = null;


var config = InformantMod.Instance?.Config ?? new InformantConfig();
if (!config.DisplayIds.GetValueOrDefault("sell-price", true)) {
if (!config.DisplayIds.GetValueOrDefault(DisplayableId, true)) {
return; // this "decorator" is deactivated
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ public Tooltip Generate(TerrainFeature input) {
}

internal static Tooltip CreateTooltip(IModHelper modHelper, Crop crop) {
var displayName = GameInformation.GetObjectDisplayName(crop.indexOfHarvest.Value);
// for some reason, ginger is displayed as weeds
var parentSheetIndex = crop.whichForageCrop.Value == ObjectIds.GingerForageCropId ? ObjectIds.Ginger : crop.indexOfHarvest.Value;
var displayName = GameInformation.GetObjectDisplayName(parentSheetIndex);
var daysLeft = CalculateDaysLeftString(modHelper, crop);
return new Tooltip($"{displayName}\n{daysLeft}") {
Icon = Icon.ForParentSheetIndex(
crop.indexOfHarvest.Value,
parentSheetIndex,
false,
IPosition.CenterRight,
new Vector2(Game1.tileSize / 2f, Game1.tileSize / 2f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,14 @@ internal int CalculateDaysLeft(FruitTree fruitTree) {
daysLeft = fruitTree.fruitsOnTree.Value <= 0 ? 1 : 0;
}
if (daysLeft > 0) {
if (fruitTree.currentLocation?.isGreenhouse.Value ?? false) {
if (fruitTree.currentLocation?.IsGreenhouse ?? false) {
// if we are in the greenhouse, we don't need to add anything for seasons
return daysLeft;
}
if (fruitTree.currentLocation?.Name.Contains("Island") ?? false) {
// if we are on the island, we don't need to add anything for seasons
return daysLeft;
}
// check that the date we are calculating is in the correct season
var futureDay = Game1.dayOfMonth + daysLeft;
var seasonsLeft = futureDay / Seasons.LengthInDays;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,40 @@ private Tooltip CreateTooltip(SObject input) {
}

internal string CalculateMinutesLeftString(SObject input) {
switch (input.MinutesUntilReady) {
if (input is Cask cask) {
return CalculateMinutesLeftStringForCask(cask);
}

var minutesUntilReady = input.MinutesUntilReady;
switch (minutesUntilReady) {
case < 0:
return _modHelper.Translation.Get("MachineTooltipGenerator.CannotBeUnloaded");
case 0:
return _modHelper.Translation.Get("MachineTooltipGenerator.Finished");
}
var minutesLeft = input.MinutesUntilReady % 60;
var hoursLeft = (input.MinutesUntilReady / 60) % 24;
var daysLeft = input.MinutesUntilReady / 60 / 24;
var minutesLeft = minutesUntilReady % 60;
var hoursLeft = (minutesUntilReady / 60) % 24;
var daysLeft = minutesUntilReady / 60 / 24;
return $"{daysLeft:D2}:{hoursLeft:D2}:{minutesLeft:D2}";
}

private string CalculateMinutesLeftStringForCask(Cask input) {
if (input.MinutesUntilReady == 1) {
return _modHelper.Translation.Get("MachineTooltipGenerator.Finished");
}
var daysForQuality = input.GetDaysForQuality(input.GetNextQuality(input.heldObject.Value.Quality));
var daysNeededForNextQuality = (int) (input.daysToMature.Value - daysForQuality);
var daysNeededTotal = (int) input.daysToMature.Value;

if (daysNeededTotal <= 0) {
// if the wine is finished, we only need "Finished" once
return _modHelper.Translation.Get("MachineTooltipGenerator.Finished");
}

var daysNeededForNextQualityString = _modHelper.Translation.Get("MachineTooltipGenerator.ForNextQuality",
new { X = CropTooltipGenerator.ToDaysLeftString(_modHelper, daysNeededForNextQuality)});
var daysNeededTotalString = _modHelper.Translation.Get("MachineTooltipGenerator.ForTotal",
new { X = CropTooltipGenerator.ToDaysLeftString(_modHelper, daysNeededTotal)});
return $"{daysNeededForNextQualityString}\n{daysNeededTotalString}";
}
}
2 changes: 2 additions & 0 deletions Informant/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"MachineTooltipGenerator.Description" : "Ein Tooltip, der die Art des Erzeugnisses und die verbleibende Zeit zeigt",
"MachineTooltipGenerator.Finished" : "Fertig",
"MachineTooltipGenerator.CannotBeUnloaded" : "Kann nicht entladen werden",
"MachineTooltipGenerator.ForNextQuality" : "{{X}} bis zur nächsten Qualität",
"MachineTooltipGenerator.ForTotal" : "{{X}} total",

"TreeTooltipGenerator" : "Bäume-Tooltip",
"TreeTooltipGenerator.Description" : "Ein Tooltip, der die Art des Baums zeigt",
Expand Down
2 changes: 2 additions & 0 deletions Informant/i18n/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"MachineTooltipGenerator.Description" : "A tooltip that displays the type of result and remaining time",
"MachineTooltipGenerator.Finished" : "Finished",
"MachineTooltipGenerator.CannotBeUnloaded" : "Cannot be unloaded",
"MachineTooltipGenerator.ForNextQuality" : "{{X}} for next quality",
"MachineTooltipGenerator.ForTotal" : "{{X}} in total",

"TreeTooltipGenerator" : "Trees Tooltip",
"TreeTooltipGenerator.Description" : "A tooltip that displays the type of tree",
Expand Down
Loading

0 comments on commit 0a72a4d

Please sign in to comment.