Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LookupAnything] Links in ItemRecipesField, FishPondDropsField, and ItemDropListField #1053

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions ChestsAnywhere/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
/****
** remote access
****/
"config.title.remote-access": "Remote access", // TODO
"config.title.remote-access": "远程访问",

"config.range.name": "箱子显示范围",
"config.range.desc": "箱子可通过菜单访问的范围。",
Expand All @@ -86,16 +86,16 @@
/****
** open menu hotkey
****/
"config.title.open-menu-hotkey": "Hotkey to open chest UI", // TODO
"config.title.open-menu-hotkey": "箱子菜单快捷键",

"config.toggle-ui-key.name": "切换UI",
"config.toggle-ui-key.desc": "显示或隐藏箱子UI的按键",

"config.reopen-last-chest.name": "Reopen last chest", // TODO
"config.reopen-last-chest.desc": "When you press the 'toggle UI' button, whether to reopen the last chest you selected yourself during the current game session.", // TODO
"config.reopen-last-chest.name": "默认打开上个箱子",
"config.reopen-last-chest.desc": "按‘切换UI’快捷键时,打开上个查看的箱子",

"config.default-category.name": "Default category", // TODO
"config.default-category.desc": "When you press the 'toggle UI' button, show the chests in this category by default (if set). This applies after 'reopen last chest'.", // TODO
"config.default-category.name": "默认分类",
"config.default-category.desc": "W按‘切换UI’快捷键时,优先显示此分类的箱子。此设定在‘默认打开上个箱子’之后起效",

/****
** controls when chest UI is open
Expand Down
5 changes: 2 additions & 3 deletions CropsAnytimeAnywhere/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
"config.grow-crops-out-of-season.name": "随时种植作物",
"config.grow-crops-out-of-season.desc": "作物是否可以在任何季节种植和生产。只有当“随处种植作物”启用时,该选项才会生效。",

// TODO
"config.use-fruit-trees-seasonal-sprites.name": "Seasonal fruit trees sprites",
"config.use-fruit-trees-seasonal-sprites.desc": "Whether fruit trees should use their normal seasonal sprite (true) or their summer sprites (false) when 'grow crops in any season' is enabled.",
"config.use-fruit-trees-seasonal-sprites.name": "季节性果树外观",
"config.use-fruit-trees-seasonal-sprites.desc": "‘随时种植作物’开启时,控制果树是否使用季节外观。不启用时默认显示夏季外观。",

"config.force-till-dirt.name": "耕种所有土地瓦片",
"config.force-till-dirt.desc": "是否所有的土地瓦片都可以用锄头耕种,即使它们正常不能耕种。",
Expand Down
4 changes: 2 additions & 2 deletions FastAnimations/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@
/****
** 'Experimental' section
****/
"config.experimental": "Experimental options", // TODO
"config.experimental-warning": "These options aren't fully stable yet, and may cause issues like freezes and crashes. Enabling any of these isn't recommended.", // TODO
"config.experimental": "实验性功能",
"config.experimental-warning": "这些功能暂且不稳定,有可能导致游戏冻结或崩溃,不推荐使用。",

"config.event.name": "事件",
"config.event.tooltip": "事件的速度。默认{{defaultValue}}x,建议{{suggestedValue}}x."
Expand Down
29 changes: 28 additions & 1 deletion LookupAnything/Components/LookupMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Pathoschild.Stardew.LookupAnything.Framework.Constants;
using Pathoschild.Stardew.LookupAnything.Framework.Fields;
using Pathoschild.Stardew.LookupAnything.Framework.Lookups;
using Pathoschild.Stardew.LookupAnything.Framework.Models;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Menus;
Expand Down Expand Up @@ -78,12 +79,18 @@ internal class LookupMenu : BaseMenu, IScrollableMenu, IDisposable
/// <summary>Click areas for link fields that open a new subject.</summary>
private readonly IDictionary<ILinkField, Rectangle> LinkFieldAreas = new Dictionary<ILinkField, Rectangle>();

/// <summary>Click areas for link text within a field that open a new subject.</summary>
private readonly IList<ICustomField> FieldsWithLinkTextArea = [];

/// <summary>Whether the game HUD was enabled when the menu was opened.</summary>
private readonly bool WasHudEnabled;

/// <summary>Whether to exit the menu on the next update tick.</summary>
private bool ExitOnNextTick;

/// <summary>Flag for <see cref="ModEntry.OnMenuChanged"/>, exit without restoring a previous <see cref="LookupMenu"/> if set.</summary>
internal bool ExitWithoutRestore = false;


/*********
** Public methods
Expand Down Expand Up @@ -236,7 +243,10 @@ public void HandleLeftClick(int x, int y)
{
// close menu when clicked outside
if (!this.isWithinBounds(x, y))
{
this.ExitWithoutRestore = true;
this.exitThisMenu();
}

// scroll up or down
else if (this.ScrollUpButton.containsPoint(x, y))
Expand All @@ -254,7 +264,19 @@ public void HandleLeftClick(int x, int y)
ISubject? subject = link.GetLinkSubject();
if (subject != null)
this.ShowNewPage(subject);
break;
return;
}
}

foreach (ICustomField field in this.FieldsWithLinkTextArea)
{
foreach (LinkTextArea linkTextArea in field.LinkTextAreas!)
{
if (linkTextArea.Rect.Contains(x, y))
{
this.ShowNewPage(linkTextArea.Subject);
return;
}
}
}
}
Expand Down Expand Up @@ -398,6 +420,11 @@ public override void draw(SpriteBatch b)
// track link area
if (field is ILinkField linkField)
this.LinkFieldAreas[linkField] = new Rectangle((int)valuePosition.X, (int)valuePosition.Y, (int)valueSize.X, (int)valueSize.Y);
// track link text areas
if (field.LinkTextAreas != null)
{
this.FieldsWithLinkTextArea.Add(field);
}

// update offset
topOffset += Math.Max(labelSize.Y, valueSize.Y);
Expand Down
15 changes: 13 additions & 2 deletions LookupAnything/Framework/Fields/FishPondDropsField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Pathoschild.Stardew.Common.UI;
using Pathoschild.Stardew.LookupAnything.Framework.Data;
using Pathoschild.Stardew.LookupAnything.Framework.Fields.Models;
using Pathoschild.Stardew.LookupAnything.Framework.Lookups;
using Pathoschild.Stardew.LookupAnything.Framework.Models;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.GameData.FishPonds;
Expand Down Expand Up @@ -38,13 +40,15 @@ internal class FishPondDropsField : GenericField
/// <param name="currentPopulation">The current population for showing unlocked drops.</param>
/// <param name="data">The fish pond data.</param>
/// <param name="preface">>The text to display before the list, if any.</param>
public FishPondDropsField(GameHelper gameHelper, string label, int currentPopulation, FishPondData data, string preface)
public FishPondDropsField(GameHelper gameHelper, string label, int currentPopulation, FishPondData data, string preface, Func<object, GameLocation?, ISubject?>? getSubjectByEntity = null)
: base(label)
{
this.GameHelper = gameHelper;
this.Drops = this.GetEntries(currentPopulation, data, gameHelper).ToArray();
this.HasValue = this.Drops.Any();
this.Preface = preface;
this.LinkTextAreas = [];
this.GetSubjectByEntity = getSubjectByEntity;
}

/// <inheritdoc />
Expand All @@ -70,6 +74,7 @@ public FishPondDropsField(GameHelper gameHelper, string label, int currentPopula
Vector2 iconSize = new Vector2(font.MeasureString("ABC").Y);
int lastGroup = -1;
bool isPrevDropGuaranteed = false;
int idx = 0;
foreach (FishPondDrop drop in this.Drops)
{
bool disabled = !drop.IsUnlocked || isPrevDropGuaranteed;
Expand Down Expand Up @@ -108,6 +113,9 @@ public FishPondDropsField(GameHelper gameHelper, string label, int currentPopula
// draw drop
bool isGuaranteed = drop.Probability > .99f;
{
bool shouldLink = this.TryGetOrAddLinkTextArea(drop.SampleItem, ref idx, out LinkTextArea? linkTextArea);
Color textColor = (shouldLink ? Color.Blue : Color.Black) * (disabled ? 0.75f : 1f);

// draw icon
spriteBatch.DrawSpriteWithin(drop.Sprite, position.X + innerIndent, position.Y + height, iconSize, Color.White * (disabled ? 0.5f : 1f));

Expand All @@ -117,7 +125,10 @@ public FishPondDropsField(GameHelper gameHelper, string label, int currentPopula
text += $" ({I18n.Generic_Range(min: drop.MinDrop, max: drop.MaxDrop)})";
else if (drop.MinDrop > 1)
text += $" ({drop.MinDrop})";
Vector2 textSize = spriteBatch.DrawTextBlock(font, text, position + new Vector2(innerIndent + iconSize.X + 5, height + 5), wrapWidth, disabled ? Color.Gray : Color.Black);
Vector2 textSize = spriteBatch.DrawTextBlock(font, text, position + new Vector2(innerIndent + iconSize.X + 5, height + 5), wrapWidth, textColor);

if (shouldLink)
linkTextArea!.Rect = new Rectangle((int)(position.X + innerIndent + iconSize.X + 5), (int)(position.Y + height + iconSize.Y / 2), (int)textSize.X, (int)textSize.Y);

// cross out if it's guaranteed not to drop
if (isPrevDropGuaranteed)
Expand Down
36 changes: 36 additions & 0 deletions LookupAnything/Framework/Fields/GenericField.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Pathoschild.Stardew.LookupAnything.Framework.Constants;
using Pathoschild.Stardew.LookupAnything.Framework.Lookups;
using Pathoschild.Stardew.LookupAnything.Framework.Models;
using StardewValley;

namespace Pathoschild.Stardew.LookupAnything.Framework.Fields;

Expand All @@ -24,6 +29,12 @@ internal class GenericField : ICustomField
/// <inheritdoc />
public bool HasValue { get; protected set; }

/// <inheritdoc />
public IList<LinkTextArea>? LinkTextAreas { get; protected set; }

/// <summary>The <see cref="ISubjectRegistry.GetSubjectByEntity"/> method, for use in populating <see cref="this.LinkTextAreas"/>.</summary>
protected Func<object, GameLocation?, ISubject?>? GetSubjectByEntity { get; set; }


/*********
** Public methods
Expand Down Expand Up @@ -55,6 +66,7 @@ public GenericField(string label, IEnumerable<IFormattedText> value, bool? hasVa
this.Label = label;
this.Value = value.ToArray();
this.HasValue = hasValue ?? this.Value?.Any() == true;
this.LinkTextAreas = null;
}

/// <summary>Draw the value (or return <c>null</c> to render the <see cref="Value"/> using the default format).</summary>
Expand Down Expand Up @@ -152,4 +164,28 @@ protected void CollapseByDefault(string linkText)
}
return I18n.List(priceStrings);
}

/// <summary>
/// Check if item should be added to link text areas, if added/updated, increment the index.
/// Make assumption that the linkable items in the field will not change over lifetime of menu, and that each item
/// will be processed by <see cref="DrawValue"/> in the same order on every draw cycle.
/// </summary>
/// <param name="entity">Entity to try to get subject and link to</param>
/// <param name="idx">Index of the link in <see cref="this.LinkTextAreas"/></param>
/// <returns></returns>
protected virtual bool TryGetOrAddLinkTextArea(object? entity, ref int idx, [NotNullWhen(true)] out LinkTextArea? linkTextArea)
{
linkTextArea = null;
if (this.GetSubjectByEntity == null || this.LinkTextAreas == null || entity == null)
return false;
if (this.GetSubjectByEntity(entity, null) is not ISubject subject)
return false;
if (this.LinkTextAreas.Count == idx)
this.LinkTextAreas.Add(new(subject));
else if (this.LinkTextAreas.Count < idx) // misalignment in index and LinkTextAreas, abort
return false;
linkTextArea = this.LinkTextAreas[idx];
idx++;
return true;
}
}
4 changes: 4 additions & 0 deletions LookupAnything/Framework/Fields/ICustomField.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Pathoschild.Stardew.LookupAnything.Framework.Models;

namespace Pathoschild.Stardew.LookupAnything.Framework.Fields;

Expand All @@ -21,6 +23,8 @@ internal interface ICustomField
/// <summary>If the field is currently collapsed, the link to click to expand it.</summary>
LinkField? ExpandLink { get; }

/// <summary>List of clickable areas that should open a new page when clicked.</summary>
IList<LinkTextArea>? LinkTextAreas { get; }

/*********
** Public methods
Expand Down
15 changes: 13 additions & 2 deletions LookupAnything/Framework/Fields/ItemDropListField.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Pathoschild.Stardew.Common;
using Pathoschild.Stardew.LookupAnything.Framework.Data;
using Pathoschild.Stardew.LookupAnything.Framework.Lookups;
using Pathoschild.Stardew.LookupAnything.Framework.Models;
using StardewValley;

namespace Pathoschild.Stardew.LookupAnything.Framework.Fields;
Expand Down Expand Up @@ -46,7 +49,7 @@ internal class ItemDropListField : GenericField
/// <param name="crossOutNonGuaranteed">Whether to cross out non-guaranteed drops.</param>
/// <param name="defaultText">The text to display if there are no items (or <c>null</c> to hide the field).</param>
/// <param name="preface">The text to display before the list, if any.</param>
public ItemDropListField(GameHelper gameHelper, string label, IEnumerable<ItemDropData> drops, bool sort = true, bool fadeNonGuaranteed = false, bool crossOutNonGuaranteed = false, string? defaultText = null, string? preface = null)
public ItemDropListField(GameHelper gameHelper, string label, IEnumerable<ItemDropData> drops, bool sort = true, bool fadeNonGuaranteed = false, bool crossOutNonGuaranteed = false, string? defaultText = null, string? preface = null, Func<object, GameLocation?, ISubject?>? getSubjectByEntity = null)
: base(label)
{
this.GameHelper = gameHelper;
Expand All @@ -59,6 +62,8 @@ public ItemDropListField(GameHelper gameHelper, string label, IEnumerable<ItemDr
this.CrossOutNonGuaranteed = crossOutNonGuaranteed;
this.Preface = preface;
this.DefaultText = defaultText;
this.LinkTextAreas = [];
this.GetSubjectByEntity = getSubjectByEntity;
}

/// <inheritdoc />
Expand All @@ -77,13 +82,16 @@ public ItemDropListField(GameHelper gameHelper, string label, IEnumerable<ItemDr
}

// list drops
int idx = 0;
Vector2 iconSize = new(font.MeasureString("ABC").Y);
foreach ((ItemDropData drop, Item item, SpriteInfo? sprite) in this.Drops)
{
// get data
bool isGuaranteed = drop.Probability > .99f;
bool shouldFade = this.FadeNonGuaranteed && !isGuaranteed;
bool shouldCrossOut = this.CrossOutNonGuaranteed && !isGuaranteed;
bool shouldLink = this.TryGetOrAddLinkTextArea(item, ref idx, out LinkTextArea? linkTextArea);
Color textColor = (shouldLink ? Color.Blue : Color.Black) * (shouldFade ? 0.75f : 1f);

// draw icon
spriteBatch.DrawSpriteWithin(sprite, position.X, position.Y + height, iconSize, shouldFade ? Color.White * 0.5f : Color.White);
Expand All @@ -94,7 +102,10 @@ public ItemDropListField(GameHelper gameHelper, string label, IEnumerable<ItemDr
text += $" ({I18n.Generic_Range(min: drop.MinDrop, max: drop.MaxDrop)})";
else if (drop.MinDrop > 1)
text += $" ({drop.MinDrop})";
Vector2 textSize = spriteBatch.DrawTextBlock(font, text, position + new Vector2(iconSize.X + 5, height + 5), wrapWidth, shouldFade ? Color.Gray : Color.Black);
Vector2 textSize = spriteBatch.DrawTextBlock(font, text, position + new Vector2(iconSize.X + 5, height + 5), wrapWidth, textColor);

if (shouldLink)
linkTextArea!.Rect = new Rectangle((int)(position.X + iconSize.X + 5), (int)((int)position.Y + height), (int)textSize.X, (int)textSize.Y);

// cross out item if it definitely won't drop
if (shouldCrossOut)
Expand Down
Loading