Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original
UIInfoSuite2-master/.gitignore
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# General Files
[Dd]esktop.ini
.D[Ss]_Store
.vscode
UIInfoSuite2-master/LICENSE.md
MIT License
Copyright (c) 2022 Tóth Ádám
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
UIInfoSuite2-master/README.md
# If you are here to install the mod
**Go to the [Releases page](https://github.com/Annosz/UIInfoSuite2/releases) on the side, where you can always find the latest release. Download the UIInfoSuite2.zip file and copy it's content to the mod folder.**

_Do **NOT** download the Source code (zip) or Source code (tar.gz). Also, do **NOT** use the green Code > Download ZIP button on the main page. These methods will only give you the source code but you will not be able to run the mod and use it with Stardew Valley!_
**If you like the mod, you can help the development by [gifting me a coffee](https://www.buymeacoffee.com/Annosz). Actually, as I'm from a corrupt Eastern European country, this is worth more like a whole dinner for me - any donation is much appreciated.**
# UI Info Suite 2
_Ongoing maintenance for the original UI Info Suite mod for Stardew Valley._
UI Info Suite provides helpful information about things around you designed to help you be aware of what's going on without feeling like you're cheating.
This mod is a rewrite of cdaragorn's UIInfoSuite mod (which is based on Demiacle's UiModSuite), so 99% of the credit goes to those guys. My contribution started after the December of 2019, when the original project became abandoned. As this is a very useful mod, I wanted to continue providing support for newer game versions, fixing bugs and adding new requested features, so here we are!
The current features include:
- Display an icon that represents the current days luck
- Display experience point gains
- Display a dynamic experience bar that changes based on your current tool or location
- Display more accurate heart levels
- Display more information on item mouse overs, including items that are still needed for bundles
- View calendar and quest billboard anywhere
- Display icons over animals that need petting
- Display crop type and days until
harvest
- Display icon when animal has item yield (milk, wool)
- Sprinkler, scarecrow, beehive and junimo hut ranges
- Display npc locations on map
- Skip the intro by pressing the Escape key
- Display an icon for Queen of Sauce when she is airing a recipe you don't already know
- Display an icon when Clint is upgrading one of your tools. Icon will tell you how long until the tool is finished and shows you which tool you are upgrading.
- Display an icon for items that can be donated to the museum for the Complete Collection achievement
- Display an icon for items that can be shipped for the Full Shipment achievement
- Display an icon for the next day's weather conditions
- ... and also a new tab added to the options menu that allows turning each individual mod on or off whenever you want.
Known issues:
- In multiplayer only the host can see the correct location of NPC's on the map. If you face this issue, use the mod NPC Map Locations by Bouhm, which solves this problem. As it is a mod specialized for extending the map, it gets priority before UI Info Suite 2 and disables this mod's map features!
Compatibility is assured with:
- NPC Map Locations by Bouhm (gets priority before UI Info Suite 2 and disables this mod's map features)
- Bigger Backpack by spacechase0
- Level Extender by DevinLematty (probably?)
- Better Farm Animal Variety by Paritee
# Translating UI Info Suite 2
The mod can be translated into any language supported by the game, and SMAPI will automatically
use the right translations.
Contributions are welcome! See [Modding:Translations](https://stardewvalleywiki.com/Modding:Translations)
on the wiki for help contributing translations.
(❑ = untranslated, ↻ = partly translated, ✓ = fully translated)
locale | status
----------- | :----------------
default | [✓](UIInfoSuite2/i18n/default.json)
Chinese | [✓](UIInfoSuite2/i18n/zh.json)
French | [↻](UIInfoSuite2/i18n/fr.json)
German | [✓](UIInfoSuite2/i18n/de.json)
Hungarian | [↻](UIInfoSuite2/i18n/hu.json)
Italian | ❑
Japanese | [✓](UIInfoSuite2/i18n/ja.json)
Korean | [↻](UIInfoSuite2/i18n/ko.json)
[Polish] | [✓](UIInfoSuite2/i18n/pl.json)
Portuguese | [✓](UIInfoSuite2/i18n/pt.json)
Russian | [✓](UIInfoSuite2/i18n/ru.json)
Spanish | [↻](UIInfoSuite2/i18n/es.json)
[Thai] | [✓](UIInfoSuite2/i18n/th.json)
Turkish | [✓](UIInfoSuite2/i18n/tr.json)
[Ukrainian] | [✓](UIInfoSuite2/i18n/uk.json)
[Polish]: https://www.nexusmods.com/stardewvalley/mods/3616
[Thai]: https://www.nexusmods.com/stardewvalley/mods/7052
[Ukrainian]: https://www.nexusmods.com/stardewvalley/mods/8427
UIInfoSuite2-master/UIInfoSuite2.sln
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30621.155
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UIInfoSuite2", "UIInfoSuite2\UIInfoSuite2.csproj", "{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Debug|x86.ActiveCfg = Debug|x86
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Debug|x86.Build.0 = Debug|x86
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Release|Any CPU.Build.0 = Release|Any CPU
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Release|x86.ActiveCfg = Release|x86
{B6DD0C36-999B-44FD-9316-C0AA6D5A6E7A}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DF47C34C-10B7-416C-8876-AC9ABD17B4EA}
EndGlobalSection
EndGlobal
UIInfoSuite2-master/UIInfoSuite2/.gitignore
/UIInfoSuite2.csproj.local
UIInfoSuite2-master/UIInfoSuite2/AdditionalFeatures/SkipIntro.cs
using System;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
namespace UIInfoSuite2.AdditionalFeatures
{
public class SkipIntro
{
private readonly IModEvents _events;
public SkipIntro(IModEvents events)
{
_events = events;
events.Input.ButtonPressed += OnButtonPressed;
events.GameLoop.SaveLoaded += OnSaveLoaded;
}
private void OnSaveLoaded(object sender, EventArgs e)
{
_events.Input.ButtonPressed -= OnButtonPressed;
_events.GameLoop.SaveLoaded -= OnSaveLoaded;
}
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
{
if (Game1.activeClickableMenu is TitleMenu menu && (e.Button == SButton.Escape || e.Button == SButton.ControllerStart))
{
menu.skipToTitleButtons();
_events.Input.ButtonPressed -= OnButtonPressed;
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Assets/LevelUp license.txt
LevelUp.wav provided courtesy of jobro on www.freesound.org under a CC BY 3.0 license.
UIInfoSuite2-master/UIInfoSuite2/Assets/LevelUp.wav
UIInfoSuite2-master/UIInfoSuite2/Compatibility/DynamicGameAssetHelper.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using StardewValley;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using UIInfoSuite2.Infrastructure.Reflection;
using SObject = StardewValley.Object;
namespace UIInfoSuite2.Compatibility
{
public class DynamicGameAssetsHelper
{
public IDynamicGameAssetsApi Api { get; init; }
private IReflectionHelper Reflection { get; init; }
private IModEvents ModEvents { get; init; }
private IMonitor Monitor { get; init; }
private Reflector Reflector { get; init; }
private Assembly? _dgaAssembly;
private DgaFakeIdRetriever? _dgaFakeId;
private DgaFakeIdRetriever DgaFakeId {
get => _dgaFakeId ??= new DgaFakeIdRetriever(this);
}
private IReflectedMethod? _modFindMethod;
public DynamicGameAssetsHelper(IDynamicGameAssetsApi api, IModHelper helper, IMonitor monitor)
{
this.Api = api;
this.Reflection = helper.Reflection;
this.ModEvents = helper.Events;
this.Monitor = monitor;
this.Reflector = new Reflector();
this.ModEvents.GameLoop.DayEnding += OnDayEnding;
}
/// <summary>Inject an object of any DGA type to get a reference to the DGA Assembly</summary>
/// <returns>The initialized <see cref="DynamicGameAssetsHelper"/>.</returns>
public DynamicGameAssetsHelper InjectDga(object dga)
{
if (_dgaAssembly == null)
{
_dgaAssembly = dga.GetType().Assembly;
Monitor.Log($"{this.GetType().Name}: Retrieved reference to DGA assemby using DGA class instance of {dga.GetType().FullName}.", LogLevel.Trace);
}
return this;
}
public void Dispose()
{
this.ModEvents.GameLoop.DayEnding -= this.OnDayEnding;
}
private void OnDayEnding(object? sender, DayEndingEventArgs e)
{
this.Reflector.NewCacheInterval();
}
/// Retrieve fake object ids for DGA object using code copy-pasted from DGA.
/// But it first checks using a roundabout way, that the copy-pasted code is still valid.
private class DgaFakeIdRetriever
{
private DynamicGameAssetsHelper DgaHelper { get; init; }
private bool? deterministicHashCodeIsCorrect
= null;
public DgaFakeIdRetriever(DynamicGameAssetsHelper dgaHelper)
{
this.DgaHelper = dgaHelper;
}
public int GetId(SObject dgaItem)
{
if (deterministicHashCodeIsCorrect == null)
{
int hashedId = this.GetIdByDeterministicHashCode(dgaItem);
int shippedId = this.GetIdByShippingIt(dgaItem);
deterministicHashCodeIsCorrect = (hashedId == shippedId);
if ((bool) deterministicHashCodeIsCorrect)
DgaHelper.Monitor.Log($"{this.GetType().Name}: The GetDeterministicHashCode implementation seems to be correct", LogLevel.Trace);
else
DgaHelper.Monitor.Log($"{this.GetType().Name}: The GetDeterministicHashCode implementation seems to be incorrect. Processing DGA items will be slower.", LogLevel.Info);
return shippedId;
}
else if (deterministicHashCodeIsCorrect == true)
{
return this.GetIdByDeterministicHashCode(dgaItem);
}
else
{
return this.GetIdByShippingIt(dgaItem);
}
}
private int GetIdByDeterministicHashCode(SObject dgaItem)
{
return this.GetDeterministicHashCode(DgaHelper.GetFullId(dgaItem)!);
}
private int GetIdByShippingIt(SObject dgaItem)
{
DgaHelper.Monitor.Log($"{this.GetType().Name}: Retrieving the fake DGA item ID for {dgaItem.Name} by shipping it.", LogLevel.Trace);
var shippingMenu = new StardewValley.Menus.ShippingMenu(new List<Item>());
// Record previous state
uint oldCropsShipped = Game1.stats.CropsShipped;
var oldBasicShipped = new Dictionary<int, int>(Game1.player.basicShipped.FieldDict.Select(x => KeyValuePair.Create(x.Key, x.Value.Value)));
// Ship the item to observe side-effects
shippingMenu.parseItems(new List<Item>{ dgaItem });
// Restore previous state
Game1.stats.CropsShipped = oldCropsShipped;
var basicShipped = Game1.player.basicShipped;
// Find the new item
List<int> newItems = new();
foreach (var shipped in basicShipped.Keys)
{
if (oldBasicShipped.TryGetValue(shipped, out int oldValue))
{
if (oldValue != basicShipped[shipped])
{
basicShipped[shipped] = oldValue;
newItems.Add(shipped);
}
}
else
{
basicShipped.Remove(shipped);
newItems.Add(shipped);
}
}
if (newItems.Count != 1)
throw new Exception($"{newItems.Count} items were shipped when we expected only one");
return newItems[0];
}
// Copied from SpaceShared.CommonExtensions
private int GetDeterministicHashCode(string str)
{
unchecked
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
break;
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return hash1 + (hash2 * 1566083941);
}
}
}
/// <summary>Mod.Find()</summary>
public object? FindPackData(string fullId)
{
var modFind = GetModFindMethod();
if (modFind == null)
throw new Exception("Could not load DynamicGameAssets.Mod.Find");
return modFind.Invoke<object?>(fullId);
}
public int GetDgaObjectFakeId(SObject dgaItem)
{
return DgaFakeId.GetId(dgaItem);
}
#region DGA instance fields, methods and properties
/// <summary>CustomCrop.Data</summary>
private object? GetCropData(object customCrop)
{
return Reflector.GetPropertyGetter<object?>(customCrop, "Data").GetValue();
}
/// <summary>IDGAItem.FullId</summary>
public string? GetFullId(object dgaItem)
{
return Reflection.GetProperty<string?>(dgaItem, "FullId").GetValue();
}
#endregion
#region Code reflecting into DGA
public SObject? GetCropHarvest(object customCrop)
{
var cropData = this.GetCropData(customCrop);
if (cropData == null)
return null;
return this.GetCropPackHarvest(cropData);
}
public SObject? GetSeedsHarvest(Item item)
{
if (!(item is StardewValley.Object seedsObject && seedsObject.Category == StardewValley.Object.SeedsCategory))
return null;
var itemData = Reflector.GetPropertyGetter<object?>(item, "Data").GetValue();
if (itemData == null)
return null;
string? itemPlants = Reflection.GetProperty<string?>(itemData, "Plants").GetValue();
if (itemPlants == null)
return null;
var cropData = this.FindPackData(itemPlants);
if (cropData == null)
return null;
return this.GetCropPackHarvest(cropData);
}
private SObject? GetCropPackHarvest(object cropData)
{
var cropPhases = Reflector.GetPropertyGetter<IList>(cropData, "Phases").GetValue();
// Find the last phase that has a harvest drop
IList? harvestDrops = null;
foreach (var phase in cropPhases)
{
var phaseDrops = Reflector.GetPropertyGetter<IList>(phase!, "HarvestedDrops").GetValue()!;
if (phaseDrops.Count > 0)
harvestDrops = phaseDrops;
}
if (harvestDrops == null)
return null;
if (harvestDrops.Count > 1)
throw new Exception("DGA crops with multiple drops on the last harvest are not supported");
var possibleDrops = Reflector.GetPropertyGetter<IList>(harvestDrops[0]!, "Item").GetValue();
if (possibleDrops.Count != 1)
throw new Exception("DGA crops with random drops are not supported");
var dropItem = Reflector.GetPropertyGetter<object?>(possibleDrops[0]!, "Value").GetValue()!;
string dropItemType = Reflector.GetPropertyGetter<Enum>(dropItem, "Type").GetValue()!.ToString()!;
string dropItemValue = Reflector.GetPropertyGetter<string?>(dropItem, "Value").GetValue()!;
if (dropItemType == "DGAItem")
return (StardewValley.Object) this.Api.SpawnDGAItem(dropItemValue);
else if (dropItemType == "VanillaItem")
return new StardewValley.Object(int.Parse(dropItemValue), 1);
else
throw new Exception("Harvest types other than DGAItem and VanillaItem are not supported");
}
#endregion
#region Code loading from DGA assembly
private IReflectedMethod? GetModFindMethod() {
if (_modFindMethod != null)
return _modFindMethod;
if (_dgaAssembly == null)
return null;
var modClass = _dgaAssembly.GetType("DynamicGameAssets.Mod")!;
_modFindMethod = Reflection.GetMethod(modClass, "Find");
return _modFindMethod;
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/Compatibility/DynamicGameAssetsEntry.cs
using StardewModdingAPI;
using StardewModdingAPI.Events;
using System;
using System.Diagnostics.CodeAnalysis;
namespace UIInfoSuite2.Compatibility
{
/// <summary>Entrypoint for all things DGA</summary>
public class DynamicGameAssetsEntry : IDisposable
{
private const string MOD_ID = "spacechase0.DynamicGameAssets";
private IModHelper Helper { get; init; }
private IMonitor Monitor { get; init; }
public IDynamicGameAssetsApi? Api { get; private set; }
private DynamicGameAssetsHelper? _dgaHelper;
public bool IsLoaded { get; private set; }
public DynamicGameAssetsEntry(IModHelper helper, IMonitor monitor)
{
this.Helper = helper;
this.Monitor = monitor;
this.Helper.Events.GameLoop.GameLaunched += OnGameLaunched;
}
public void Dispose()
{
this.Helper.Events.GameLoop.GameLaunched -= OnGameLaunched;
}
private void OnGameLaunched(object? sender, GameLaunchedEventArgs e)
{
// Check if DGA is loaded
if (Helper.ModRegistry.IsLoaded(MOD_ID))
{
this.IsLoaded = true;
// Get DGA's API
var api = Helper.ModRegistry.GetApi<IDynamicGameAssetsApi>(MOD_ID);
if (api != null)
{
this.Api = api;
this._dgaHelper = new DynamicGameAssetsHelper(Api, Helper, Monitor);
}
}
}
/// <summary>Check if <paramref name="obj"/> is a DGA CustomCrop and provide a <see cref="DynamicGameAssetsHelper"/></summary>
public bool IsCustomCrop(object obj, [NotNullWhen(true)] out DynamicGameAssetsHelper? dgaHelper)
{
dgaHelper = null;
if (this.IsLoaded && obj.GetType().FullName == "DynamicGameAssets.Game.CustomCrop")
dgaHelper = _dgaHelper?.InjectDga(obj);
return dgaHelper != null;
}
/// <summary>Check if <paramref name="obj"/> is a DGA CustomObject and provide a <see cref="DynamicGameAssetsHelper"/></summary>
public bool IsCustomObject(object obj, [NotNullWhen(true)] out DynamicGameAssetsHelper? dgaHelper)
{
dgaHelper = null;
if (this.IsLoaded && obj.GetType().FullName == "DynamicGameAssets.Game.CustomObject")
dgaHelper = _dgaHelper?.InjectDga(obj);
return dgaHelper != null;
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Compatibility/ExperienceBar.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
using System;
// ReSharper disable once CheckNamespace
namespace UIInfoSuite2.UIElements
{
// This part of the class is only user for compatibility reasons
public partial class ExperienceBar
{
// For providing compatibility for mods that patch this method
// TODO: When refactoring is done, mark this as deprecated and provide a long-term new method
// Notable dependents: MARGO -- Modular Gameplay Overhaul
private void DrawExperienceBar(int barWidth, int experienceGainedThisLevel, int experienceRequiredForNextLevel, int currentLevel)
{
float leftSide = Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Left;
if (Game1.isOutdoorMapSmallerThanViewport())
{
int num3 = Game1.currentLocation.map.Layers[0].LayerWidth * Game1.tileSize;
leftSide += (Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Right - num3) / 2;
}
Game1.drawDialogueBox(
(int)leftSide,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 160,
240,
160,
false,
true);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
barWidth,
31),
_experienceFillColor.Value);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
Math.Min(4, barWidth),
31),
_experienceFillColor.Value);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
barWidth,
4),
_experienceFillColor.Value);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 36,
barWidth,
4),
_experienceFillColor.Value);
ClickableTextureComponent textureComponent =
new ClickableTextureComponent(
"",
new Rectangle(
(int)leftSide - 36,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 80,
260,
100),
"",
"",
Game1.mouseCursors,
new Rectangle(0, 0, 0, 0),
Game1.pixelZoom);
if (textureComponent.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
{
Game1.drawWithBorder(
experienceGainedThisLevel + "/" + experienceRequiredForNextLevel,
Color.Black,
Color.Black,
new Vector2(
leftSide + 33,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 70));
}
else
{
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(
leftSide + 54,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 62),
_levelUpIconRectangle.Value,
_experienceFillColor.Value,
0,
Vector2.Zero,
2.9f,
SpriteEffects.None,
0.85f);
Game1.drawWithBorder(
currentLevel.ToString(),
Color.Black * 0.6f,
Color.Black,
new Vector2(
leftSide + 33,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 70));
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Compatibility/IDynamicGameAssetsApi.cs
namespace UIInfoSuite2.Compatibility
{
public interface IDynamicGameAssetsApi
{
public object SpawnDGAItem(string fullId);
}
}
UIInfoSuite2-master/UIInfoSuite2/Compatibility/IGenericModConfigMenuApi.cs
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Utilities;
using StardewValley;
namespace UIInfoSuite2.Compatibility
{
/// <summary>The API which lets other mods add a config UI through Generic Mod Config Menu.</summary>
public interface IGenericModConfigMenuApi
{
/*********
** Methods
*********/
/****
** Must be called first
****/
/// <summary>Register a mod whose config can be edited through the UI.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="reset">Reset the mod's config to its default values.</param>
/// <param name="save">Save the mod's current config to the <c>config.json</c> file.</param>
/// <param name="titleScreenOnly">Whether the options can only be edited from the title screen.</param>
/// <remarks>Each mod can only be registered once, unless it's deleted via <see cref="Unregister"/> before calling this again.</remarks>
void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false);
/****
** Basic options
****/
/// <summary>Add a section title at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="text">The title text shown in the form.</param>
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the title, or <c>null</c> to disable the tooltip.</param>
void AddSectionTitle(IManifest mod, Func<string> text, Func<string> tooltip = null);
/// <summary>Add a paragraph of text at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="text">The paragraph text to display.</param>
void AddParagraph(IManifest mod, Func<string> text);
/// <summary>Add an image at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="texture">The image texture to display.</param>
/// <param name="texturePixelArea">The pixel area within the texture to display, or <c>null</c> to show the entire image.</param>
/// <param name="scale">The zoom factor to apply to the image.</param>
void AddImage(IManifest mod, Func<Texture2D> texture, Rectangle? texturePixelArea = null, int scale = Game1.pixelZoom);
/// <summary>Add a boolean option at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddBoolOption(IManifest mod, Func<bool> getValue, Action<bool> setValue, Func<string> name, Func<string> tooltip = null, string fieldId = null);
/// <summary>Add an integer option at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="min">The minimum allowed value, or <c>null</c> to allow any.</param>
/// <param name="max">The maximum allowed value, or <c>null</c> to allow any.</param>
/// <param name="interval">The interval of values that can be selected.</param>
/// <param name="formatValue">Get the display text to show for a value, or <c>null</c> to show the number as-is.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddNumberOption(IManifest mod, Func<int> getValue, Action<int> setValue, Func<string> name, Func<string> tooltip = null, int? min = null, int? max = null, int? interval = null, Func<int, string> formatValue = null, string fieldId = null);
/// <summary>Add a float option at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="min">The minimum allowed value, or <c>null</c> to allow any.</param>
/// <param name="max">The maximum allowed value, or <c>null</c> to allow any.</param>
/// <param name="interval">The interval of values that can be selected.</param>
/// <param name="formatValue">Get the display text to show for a value, or <c>null</c> to show the number as-is.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddNumberOption(IManifest mod, Func<float> getValue, Action<float> setValue, Func<string> name, Func<string> tooltip = null, float? min = null, float? max = null, float? interval = null, Func<float, string> formatValue = null, string fieldId = null);
/// <summary>Add a string option at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="allowedValues">The values that can be selected, or <c>null</c> to allow any.</param>
/// <param name="formatAllowedValue">Get the display text to show for a value from <paramref name="allowedValues"/>, or <c>null</c> to show the values as-is.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddTextOption(IManifest mod, Func<string> getValue, Action<string> setValue, Func<string> name, Func<string> tooltip = null, string[] allowedValues = null, Func<string, string> formatAllowedValue = null, string fieldId = null);
/// <summary>Add a key binding at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddKeybind(IManifest mod, Func<SButton> getValue, Action<SButton> setValue, Func<string> name, Func<string> tooltip = null, string fieldId = null);
/// <summary>Add a key binding list at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="getValue">Get the current value from the mod config.</param>
/// <param name="setValue">Set a new value in the mod config.</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, or <c>null</c> to disable the tooltip.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
void AddKeybindList(IManifest mod, Func<KeybindList> getValue, Action<KeybindList> setValue, Func<string> name, Func<string> tooltip = null, string fieldId = null);
/****
** Multi-page management
****/
/// <summary>Start a new page in the mod's config UI, or switch to that page if it already exists. All options registered after this will be part of that page.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="pageId">The unique page ID.</param>
/// <param name="pageTitle">The page title shown in its UI, or <c>null</c> to show the <paramref name="pageId"/> value.</param>
/// <remarks>You must also call <see cref="AddPageLink"/> to make the page accessible. This is only needed to set up a multi-page config UI. If you don't call this method, all options will be part of the mod's main config UI instead.</remarks>
void AddPage(IManifest mod, string pageId, Func<string> pageTitle = null);
/// <summary>Add a link to a page added via <see cref="AddPage"/> at the current position in the form.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="pageId">The unique ID of the page to open when the link is clicked.</param>
/// <param name="text">The link text shown in the form.</param>
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the link, or <c>null</c> to disable the tooltip.</param>
void AddPageLink(IManifest mod, string pageId, Func<string> text, Func<string> tooltip = null);
/****
** Advanced
****/
/// <summary>Add an option at the current position in the form using custom rendering logic.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="name">The label text to show in the form.</param>
/// <param name="draw">Draw the option in the config UI. This is called with the sprite batch being rendered and the pixel position at which to start drawing.</param>
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the field, or <c>null</c> to disable the tooltip.</param>
/// <param name="beforeMenuOpened">A callback raised just before the menu containing this option is opened.</param>
/// <param name="beforeSave">A callback raised before the form's current values are saved to the config (i.e. before the <c>save</c> callback passed to <see cref="Register"/>).</param>
/// <param name="afterSave">A callback raised after the form's current values are saved to the config (i.e. after the <c>save</c> callback passed to <see cref="Register"/>).</param>
/// <param name="beforeReset">A callback raised before the form is reset to its default values (i.e. before the <c>reset</c> callback passed to <see cref="Register"/>).</param>
/// <param name="afterReset">A callback raised after the form is reset to its default values (i.e. after the <c>reset</c> callback passed to <see cref="Register"/>).</param>
/// <param name="beforeMenuClosed">A callback raised just before the menu containing this option is closed.</param>
/// <param name="height">The pixel height to allocate for the option in the form, or <c>null</c> for a standard input-sized option. This is called and cached each time the form is opened.</param>
/// <param name="fieldId">The unique field ID for use with <see cref="OnFieldChanged"/>, or <c>null</c> to auto-generate a randomized ID.</param>
/// <remarks>The custom logic represented by the callback parameters is responsible for managing its own state if needed. For example, you can store state in a static field or use closures to use a state variable.</remarks>
void AddComplexOption(IManifest mod, Func<string> name, Action<SpriteBatch, Vector2> draw, Func<string> tooltip = null, Action beforeMenuOpened = null, Action beforeSave = null, Action afterSave = null, Action beforeReset = null, Action afterReset = null, Action beforeMenuClosed = null, Func<int> height = null, string fieldId = null);
/// <summary>Set whether the options registered after this point can only be edited from the title screen.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="titleScreenOnly">Whether the options can only be edited from the title screen.</param>
/// <remarks>This lets you have different values per-field. Most mods should just set it once in <see cref="Register"/>.</remarks>
void SetTitleScreenOnlyForNextOptions(IManifest mod, bool titleScreenOnly);
/// <summary>Register a method to notify when any option registered by this mod is edited through the config UI.</summary>
/// <param name="mod">The mod's manifest.</param>
/// <param name="onChange">The method to call with the option's unique field ID and new value.</param>
/// <remarks>Options use a randomized ID by default; you'll likely want to specify the <c>fieldId</c> argument when adding options if you use this.</remarks>
void OnFieldChanged(IManifest mod, Action<string, object> onChange);
/// <summary>Open the config UI for a specific mod.</summary>
/// <param name="mod">The mod's manifest.</param>
void OpenModMenu(IManifest mod);
/// <summary>Get the currently-displayed mod config menu, if any.</summary>
/// <param name="mod">The manifest of the mod whose config menu is being shown, or <c>null</c> if not applicable.</param>
/// <param name="page">The page ID being shown for the current config menu, or <c>null</c> if not applicable. This may be <c>null</c> even if a mod config menu is shown (e.g. because the mod doesn't have pages).</param>
/// <returns>Returns whether a mod config menu is being shown.</returns>
bool TryGetCurrentMenu(out IManifest mod, out string page);
/// <summary>Remove a mod from the config UI and delete all its options and pages.</summary>
/// <param name="mod">The mod's manifest.</param>
void Unregister(IManifest mod);
}
}
UIInfoSuite2-master/UIInfoSuite2/Compatibility/ILevelExtender.cs
namespace UIInfoSuite2.Compatibility
{
public interface ILevelExtender
{
int[] CurrentXP();
int[] RequiredXP();
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/Extensions/CollectionExtensions.cs
using System.Collections.Generic;
namespace UIInfoSuite2.Infrastructure.Extensions
{
public static class CollectionExtensions
{
public static TValue SafeGet<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default)
{
TValue value = defaultValue;
if (dictionary != null)
{
if (!dictionary.TryGetValue(key, out value))
value = defaultValue;
}
return value;
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/Extensions/ObjectExtensions.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewValley;
using System.Collections.Generic;
namespace UIInfoSuite2.Infrastructure.Extensions
{
public static class ObjectExtensions
{
#region Properties
private static readonly Dictionary<string, int> NpcHeadShotSize = new()
{
{ "Piere", 9 },
{ "Sebastian", 7 },
{ "Evelyn", 5 },
{ "Penny", 6 },
{ "Jas", 6 },
{ "Caroline", 5 },
{ "Dwarf", 5 },
{ "Sam", 9 },
{ "Maru", 6 },
{ "Wizard", 9 },
{ "Jodi", 7 },
{ "Krobus", 7 },
{ "Alex", 8 },
{ "Kent", 10 },
{ "Linus", 4 },
{ "Harvey", 9 },
{ "Shane", 8 },
{ "Haley", 6 },
{ "Robin", 7 },
{ "Marlon", 2 },
{ "Emily", 8 },
{ "Marnie", 5 },
{ "Abigail", 7 },
{ "Leah", 6 },
{ "George", 5 },
{ "Elliott", 9 },
{ "Gus", 7 },
{ "Lewis", 8 },
{ "Demetrius", 11 },
{ "Pam", 5 },
{ "Vincent", 6 },
{ "Sandy", 7 },
{ "Clint", 10 },
{ "Willy", 10 }
};
#endregion
public static Rectangle GetHeadShot(this NPC npc)
{
int size;
if (!NpcHeadShotSize.TryGetValue(npc.Name, out size))
size = 4;
Rectangle mugShotSourceRect = npc.getMugShotSourceRect();
mugShotSourceRect.Height -= size / 2;
mugShotSourceRect.Y -= size / 2;
return mugShotSourceRect;
}
public static string SafeGetString(this IModHelper helper, string key)
{
var result = string.Empty;
if (!string.IsNullOrEmpty(key) &&
helper != null)
{
result = helper.Translation.Get(key);
}
return result;
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/Extensions/StringExtensions.cs
namespace UIInfoSuite2.Infrastructure.Extensions
{
internal static class StringExtensions
{
public static int SafeParseInt32(this string s)
{
int result = 0;
if (!string.IsNullOrWhiteSpace(s))
{
int.TryParse(s, out result);
}
return result;
}
public static int SafeParseInt64(this string s)
{
int result = 0;
if (!string.IsNullOrWhiteSpace(s))
int.TryParse(s, out result);
return result;
}
public static bool SafeParseBool(this string s)
{
bool result = false;
if (!string.IsNullOrWhiteSpace(s))
{
bool.TryParse(s, out result);
}
return result;
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/IconHandler.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI.Utilities;
using StardewValley;
using System;
namespace UIInfoSuite2.Infrastructure
{
public sealed class IconHandler
{
public static IconHandler Handler { get; } = new();
private readonly PerScreen<int> _amountOfVisibleIcons = new();
private IconHandler()
{
}
public Point GetNewIconPosition()
{
int yPos = Game1.options.zoomButtons ? 290 : 260;
int xPosition = Tools.GetWidthInPlayArea() - 70 - 48 * _amountOfVisibleIcons.Value;
if (Game1.player.questLog.Any() || Game1.player.team.specialOrders.Any())
{
xPosition -= 65;
}
++_amountOfVisibleIcons.Value;
return new Point(xPosition, yPos);
}
public void Reset(object sender, EventArgs e)
{
_amountOfVisibleIcons.Value = 0;
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/LanguageKeys.cs
namespace UIInfoSuite2.Infrastructure
{
public static class LanguageKeys
{
public const string Days = "Days";
public const string DaysToMature = "DaysToMature";
public const string Hours = "Hours";
public const string Minutes = "Minutes";
public const string ReadyToHarvest = "ReadyToHarvest";
public const string TodaysRecipe = "TodaysRecipe";
public const string TravelingMerchantIsInTown = "TravelingMerchantIsInTown";
public const string RainNextDay = "RainNextDay";
public const string ThunderstormNextDay = "ThunderstormNextDay";
public const string SnowNextDay = "SnowNextDay";
public const string IslandRainNextDay = "IslandRainNextDay";
public const string IslandThunderstormNextDay = "IslandThunderstormNextDay";
public const string HarvestPrice = "HarvestPrice";
public const string LevelUp = "LevelUp";
public const string Calendar = "Calendar";
public const string Billboard = "Billboard";
public const string DaysUntilToolIsUpgraded = "DaysUntilToolIsUpgraded";
public const string ToolIsFinishedBeingUpgraded = "ToolIsFinishedBeingUpgraded";
public const string DailyLuckValue = "DailyLuckValue";
public const string LuckStatus1 = "LuckStatus1";
public const string LuckStatus2 = "LuckStatus2";
public const string LuckStatus3 = "LuckStatus3";
public const string LuckStatus4 = "LuckStatus4";
public const string LuckStatus5 = "LuckStatus5";
public const string LuckStatus6 = "LuckStatus6";
public const string RobinBuildingStatus = "RobinBuildingStatus";
public const string RobinHouseUpgradeStatus = "RobinHouseUpgradeStatus";
public const string NpcBirthday = "NpcBirthday";
public const string CanFindSalmonberry = "CanFindSalmonberry";
public const string CanFindBlackberry = "CanFindBlackberry";
public const string CanFindHazelnut = "CanFindHazelnut";
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/Reflection.cs
using System;
using System.Collections.Generic;
using System.Reflection;
/// Reflector#GetPropertyGetter<TValue> provides cached readonly access to properties through reflection.
/// Where TValue can be a supertype of the actual property type.
/// Based on SMAPI's Reflector class.
namespace UIInfoSuite2.Infrastructure.Reflection
{
public interface IReflectedGetProperty<TValue>
{
PropertyInfo PropertyInfo { get; }
TValue GetValue();
}
public class ReflectedGetProperty<TValue> : IReflectedGetProperty<TValue>
{
private readonly string DisplayName;
private readonly Func<TValue>? GetMethod;
public PropertyInfo PropertyInfo { get; }
public ReflectedGetProperty(Type parentType, object? obj, PropertyInfo property, bool isStatic)
{
// validate input
if (parentType == null)
throw new ArgumentNullException(nameof(parentType));
if (property == null)
throw new ArgumentNullException(nameof(property));
// validate static
if (isStatic && obj != null)
throw new ArgumentException("A static property cannot have an object instance.");
if (!isStatic && obj == null)
throw new ArgumentException("A non-static property must have an object instance.");
this.DisplayName = $"{parentType.FullName}::{property.Name}";
this.PropertyInfo = property;
if (this.PropertyInfo.GetMethod != null)
{
try
{
this.GetMethod = (Func<TValue>) Delegate.CreateDelegate(typeof(Func<TValue>), obj, this.PropertyInfo.GetMethod);
}
catch (ArgumentException)
{
if (this.PropertyInfo.PropertyType.IsEnum)
{
Type enumType = this.PropertyInfo.PropertyType;
this.GetMethod = delegate () {
var enumDelegate = Delegate.CreateDelegate(typeof(Func<>).MakeGenericType(Enum.GetUnderlyingType(enumType)), obj, this.PropertyInfo.GetMethod);
return (TValue) Enum.ToObject(enumType, enumDelegate.DynamicInvoke()!);
};
}
else
{
throw;
}
}
}
}
public TValue GetValue()
{
if (this.GetMethod
== null)
throw new InvalidOperationException($"The {this.DisplayName} property has no get method.");
try
{
return this.GetMethod();
}
catch (InvalidCastException)
{
throw new InvalidCastException($"Can't convert the {this.DisplayName} property from {this.PropertyInfo.PropertyType.FullName} to {typeof(TValue).FullName}.");
}
catch (Exception ex)
{
throw new Exception($"Couldn't get the value of the {this.DisplayName} property", ex);
}
}
}
public class Reflector
{
public class IntervalMemoryCache<TKey, TValue>
where TKey : notnull
{
private Dictionary<TKey, TValue> HotCache = new();
private Dictionary<TKey, TValue> StaleCache = new();
public TValue GetOrSet(TKey cacheKey, Func<TValue> get)
{
// from hot cache
if (this.HotCache.TryGetValue(cacheKey, out TValue? value))
return value;
// from stale cache
if (this.StaleCache.TryGetValue(cacheKey, out value))
{
this.HotCache[cacheKey] = value;
return value;
}
// new value
value = get();
this.HotCache[cacheKey] = value;
return value;
}
public void StartNewInterval()
{
this.StaleCache.Clear();
if (this.HotCache.Count is not 0)
(this.StaleCache, this.HotCache) = (this.HotCache, this.StaleCache); // swap hot cache to stale
}
}
private readonly IntervalMemoryCache<string, MemberInfo?> Cache = new();
public void NewCacheInterval()
{
this.Cache.StartNewInterval();
}
public IReflectedGetProperty<TValue> GetPropertyGetter<TValue>(object obj, string name, bool required = true)
{
// validate
if (obj == null)
throw new ArgumentNullException(nameof(obj), "Can't get a instance property from a null object.");
// get property from hierarchy
IReflectedGetProperty<TValue>? property = this.GetGetPropertyFromHierarchy<TValue>(obj.GetType(), obj, name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (required && property == null)
throw new InvalidOperationException($"The {obj.GetType().FullName} object doesn't have a '{name}' instance property.");
return property!;
}
private IReflectedGetProperty<TValue>? GetGetPropertyFromHierarchy<TValue>(Type type, object? obj, string name, BindingFlags bindingFlags)
{
bool isStatic = bindingFlags.HasFlag(BindingFlags.Static);
PropertyInfo? property = this.GetCached(
'p', type, name, isStatic,
fetch: () =>
{
for (Type? curType = type; curType != null; curType = curType.BaseType)
{
PropertyInfo? propertyInfo = curType.GetProperty(name, bindingFlags);
if (propertyInfo != null)
{
type = curType;
return propertyInfo;
}
}
return null;
}
);
return property != null
? new ReflectedGetProperty<TValue>(type, obj, property, isStatic)
: null;
}
private TMemberInfo? GetCached<TMemberInfo>(char memberType, Type type, string memberName, bool isStatic, Func<TMemberInfo?> fetch)
where TMemberInfo : MemberInfo
{
string key = $"{memberType}{(isStatic ? 's' : 'i')}{type.FullName}:{memberName}";
return (TMemberInfo?) this.Cache.GetOrSet(key, fetch);
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Infrastructure/Tools.cs
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Menus;
using SObject = StardewValley.Object;
namespace UIInfoSuite2.Infrastructure
{
public static class Tools
{
public static void CreateSafeDelayedDialogue(string dialogue, int timer)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(timer);
do
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
while (Game1.activeClickableMenu is GameMenu);
Game1.setDialogue(dialogue, true);
});
}
public static int GetWidthInPlayArea()
{
if (Game1.isOutdoorMapSmallerThanViewport())
{
int right = Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Right;
int totalWidth = Game1.currentLocation.map.Layers[0].LayerWidth * Game1.tileSize;
int someOtherWidth = Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Right - totalWidth;
return right - someOtherWidth / 2;
}
else
{
return Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Right;
}
}
public static int GetSellToStorePrice(Item item)
{
if (item is SObject obj)
{
return obj.sellToStorePrice();
}
else
{
return item.salePrice() / 2;
}
}
public static SObject? GetHarvest(Item item)
{
if (item is SObject seedsObject
&& seedsObject.Category == StardewValley.Object.SeedsCategory
&& seedsObject.ParentSheetIndex != Crop.mixedSeedIndex)
{
if (seedsObject.isSapling())
{
var tree = new StardewValley.TerrainFeatures.FruitTree(seedsObject.ParentSheetIndex);
return new SObject(tree.indexOfFruit.Value, 1);
}
else if (ModEntry.DGA.IsCustomObject(item, out var dgaHelper))
{
try
{
return dgaHelper.GetSeedsHarvest(item);
}
catch (Exception e)
{
string? itemId = null;
try
{
itemId = dgaHelper.GetFullId(item);
}
catch (Exception catchException)
{
ModEntry.MonitorObject.Log(catchException.ToString(), LogLevel.Trace);
}
ModEntry.MonitorObject.LogOnce($"An error occured while fetching the harvest for {itemId ?? "unknownItem"}", LogLevel.Error);
ModEntry.MonitorObject.Log(e.ToString(), LogLevel.Debug);
return null;
}
}
else
{
var crop = new Crop(seedsObject.ParentSheetIndex, 0, 0);
return new SObject(crop.indexOfHarvest.Value, 1);
}
} else {
return null;
}
}
public static int GetHarvestPrice(Item item)
{
return GetHarvest(item)?.sellToStorePrice() ?? 0;
}
public static void DrawMouseCursor()
{
if (!Game1.options.hardwareCursor)
{
int mouseCursorToRender = Game1.options.gamepadControls ? Game1.mouseCursor +
44 : Game1.mouseCursor;
var what = Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, mouseCursorToRender, 16, 16);
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(Game1.getMouseX(), Game1.getMouseY()),
what,
Color.White,
0.0f,
Vector2.Zero,
Game1.pixelZoom + (Game1.dialogueButtonScale / 150.0f),
SpriteEffects.None,
1f);
}
}
public static Item? GetHoveredItem()
{
Item? hoverItem = null;
if (Game1.activeClickableMenu == null && Game1.onScreenMenus != null)
{
foreach (var menu in Game1.onScreenMenus)
{
if (menu is Toolbar toolbar)
{
FieldInfo hoverItemField = typeof(Toolbar).GetField("hoverItem", BindingFlags.Instance | BindingFlags.NonPublic);
hoverItem = hoverItemField.GetValue(toolbar) as Item;
}
}
}
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.GetCurrentPage() is InventoryPage inventory)
{
FieldInfo hoveredItemField = typeof(InventoryPage).GetField("hoveredItem", BindingFlags.Instance | BindingFlags.NonPublic);
hoverItem = hoveredItemField.GetValue(inventory) as Item;
}
if (Game1.activeClickableMenu is ItemGrabMenu itemMenu)
{
hoverItem = itemMenu.hoveredItem;
}
return hoverItem;
}
public static void GetSubTexture(Color[] output, Color[] originalColors, Rectangle sourceBounds, Rectangle clipArea)
{
if (output.Length < clipArea.Width * clipArea.Height)
{
return;
}
var dest = 0;
for (var yOffset = 0; yOffset < clipArea.Height; yOffset++)
{
for (var xOffset = 0; xOffset < clipArea.Width; xOffset++)
{
var idx = (clipArea.X + xOffset) + (sourceBounds.Width * (yOffset + clipArea.Y));
output[dest++] = originalColors[idx];
}
}
}
public static void SetSubTexture(Color[] sourceColors, Color[] destColors, int destWidth, Rectangle destBounds, bool overlay = false)
{
if(sourceColors.Length > destColors.Length || (destBounds.Width * destBounds.Height) > destColors.Length) {
return;
}
var emptyColor = new Color(0, 0, 0, 0);
var srcIdx = 0;
for (var yOffset = 0; yOffset < destBounds.Height; yOffset++)
{
for (var xOffset = 0; xOffset < destBounds.Width; xOffset++)
{
var idx = (destBounds.X + xOffset) + (destWidth * (yOffset + destBounds.Y));
Color sourcePixel = sourceColors[srcIdx++];
// If using overlay mode, don't copy transparent pixels
if (overlay && emptyColor.Equals(sourcePixel))
{
continue;
}
destColors[idx] = sourcePixel;
}
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/ModEntry.cs
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.AdditionalFeatures;
using UIInfoSuite2.Compatibility;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Options;
namespace UIInfoSuite2
{
public class ModEntry : Mod
{
#region Properties
public static IMonitor MonitorObject { get; private set; }
public static DynamicGameAssetsEntry DGA { get; private set; }
private static SkipIntro _skipIntro; // Needed so GC won't throw away object with subscriptions
private static ModConfig _modConfig;
private ModOptions _modOptions;
private ModOptionsPageHandler _modOptionsPageHandler;
private static EventHandler<ButtonsChangedEventArgs> _calendarAndQuestKeyBindingsHandler;
#endregion
#region Entry
public override void Entry(IModHelper helper)
{
MonitorObject = Monitor;
DGA = new DynamicGameAssetsEntry(Helper, Monitor);
_skipIntro = new SkipIntro(helper.Events);
_modConfig = Helper.ReadConfig<ModConfig>();
helper.Events.GameLoop.ReturnedToTitle += OnReturnedToTitle;
helper.Events.GameLoop.SaveLoaded += OnSaveLoaded;
helper.Events.GameLoop.Saved += OnSaved;
helper.Events.GameLoop.GameLaunched += OnGameLaunched;
helper.Events.Display.Rendering += IconHandler.Handler.Reset;
}
#endregion
#region Event subscriptions
private void OnReturnedToTitle(object sender, ReturnedToTitleEventArgs e)
{
// Unload if the main player quits.
if (Context.ScreenId != 0) return;
_modOptionsPageHandler?.Dispose();
_modOptionsPageHandler = null;
}
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
// Only load once for split screen.
if (Context.ScreenId != 0) return;
_modOptions = Helper.Data.ReadJsonFile<ModOptions>($"data/{Constants.SaveFolderName}.json")
?? Helper.Data.ReadJsonFile<ModOptions>($"data/{_modConfig.ApplyDefaultSettingsFromThisSave}.json")
?? new ModOptions();
_modOptionsPageHandler = new ModOptionsPageHandler(Helper, _modOptions, _modConfig.ShowOptionsTabInMenu);
}
private void OnSaved(object sender, EventArgs e)
{
// Only save for the main player.
if (Context.ScreenId != 0) return;
Helper.Data.WriteJsonFile($"data/{Constants.SaveFolderName}.json", _modOptions);
}
public static void RegisterCalendarAndQuestKeyBindings(IModHelper helper, bool subscribe)
{
if (_calendarAndQuestKeyBindingsHandler == null)
_calendarAndQuestKeyBindingsHandler = (object sender, ButtonsChangedEventArgs e) => HandleCalendarAndQuestKeyBindings(helper);
helper.Events.Input.ButtonsChanged -= _calendarAndQuestKeyBindingsHandler;
if (subscribe)
{
helper.Events.Input.ButtonsChanged += _calendarAndQuestKeyBindingsHandler;
}
}
private static void HandleCalendarAndQuestKeyBindings(IModHelper helper)
{
if (_modConfig != null)
{
if (Context.IsPlayerFree && _modConfig.OpenCalendarKeybind.JustPressed())
{
helper.Input.SuppressActiveKeybinds(_modConfig.OpenCalendarKeybind);
Game1.activeClickableMenu = new Billboard(false);
}
else if (Context.IsPlayerFree && _modConfig.OpenQuestBoardKeybind.JustPressed())
{
helper.Input.SuppressActiveKeybinds(_modConfig.OpenQuestBoardKeybind);
Game1.RefreshQuestOfTheDay();
Game1.activeClickableMenu = new Billboard(true);
}
}
}
#endregion
#region Generic mod config menu
private void OnGameLaunched(object sender, GameLaunchedEventArgs e)
{
// get Generic Mod Config Menu's API (if it's installed)
var modVersion = Helper.ModRegistry.Get("spacechase0.GenericModConfigMenu")?.Manifest?.Version;
var minModVersion = "1.6.0";
if (modVersion?.IsOlderThan(minModVersion) == true)
{
Monitor.Log($"Detected Generic Mod Config
Menu {modVersion} but expected {minModVersion} or newer. Disabling integration with that mod.", LogLevel.Warn);
return;
}
var configMenu = Helper.ModRegistry.GetApi<IGenericModConfigMenuApi>("spacechase0.GenericModConfigMenu");
if (configMenu is null)
return;
// register mod
configMenu.Register(
mod: ModManifest,
reset: () => _modConfig = new ModConfig(),
save: () => Helper.WriteConfig(_modConfig)
);
// add some config options
configMenu.AddBoolOption(
mod: ModManifest,
name: () => "Show options in in-game menu",
tooltip: () => "Enables an extra tab in the in-game menu where you can configure every options for this mod.",
getValue: () => _modConfig.ShowOptionsTabInMenu,
setValue: value => _modConfig.ShowOptionsTabInMenu = value
);
configMenu.AddTextOption(
mod: ModManifest,
name: () => "Apply default settings from this save",
tooltip: () => "New characters will inherit the settings for the mod from this save file.",
getValue: () => _modConfig.ApplyDefaultSettingsFromThisSave,
setValue: value => _modConfig.ApplyDefaultSettingsFromThisSave = value
);
configMenu.AddKeybindList(
mod: ModManifest,
name: () => "Open calendar keybind",
tooltip: () => "Opens the calendar tab.",
getValue: () => _modConfig.OpenCalendarKeybind,
setValue: value => _modConfig.OpenCalendarKeybind = value
);
configMenu.AddKeybindList(
mod: ModManifest,
name: () => "Open quest board keybind",
tooltip: () => "Opens the quest board.",
getValue: () => _modConfig.OpenQuestBoardKeybind,
setValue: value => _modConfig.OpenQuestBoardKeybind = value
);
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModConfig.cs
using StardewModdingAPI;
using StardewModdingAPI.Utilities;
namespace UIInfoSuite2.Options
{
internal class ModConfig
{
public bool ShowOptionsTabInMenu { get; set; } = true;
public string ApplyDefaultSettingsFromThisSave { get; set; } = "JohnDoe_123456789";
public KeybindList OpenCalendarKeybind { get; set; } = KeybindList.ForSingle(SButton.B);
public KeybindList OpenQuestBoardKeybind { get; set; } = KeybindList.ForSingle(SButton.H);
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptions.cs
using System.Collections.Generic;
namespace UIInfoSuite2.Options
{
internal record ModOptions
{
public bool AllowExperienceBarToFadeOut { get; set; } = true;
public bool ShowExperienceBar { get; set; } = true;
public bool ShowExperienceGain { get; set; } = true;
public bool ShowLevelUpAnimation { get; set; } = true;
public bool ShowHeartFills { get; set; } = true;
public bool ShowExtraItemInformation { get; set; } = true;
public bool ShowLocationOfTownsPeople { get; set; } = true;
public bool ShowLuckIcon { get; set; } = true;
public bool ShowTravelingMerchant { get; set; } = true;
public bool ShowRainyDay { get; set; } = true;
public bool ShowCropAndBarrelTooltip { get; set; } = true;
public bool ShowBirthdayIcon { get; set; } = true;
public bool ShowAnimalsNeedPets { get; set; } = true;
public bool HideAnimalPetOnMaxFriendship { get; set; } = true;
public bool ShowItemEffectRanges { get; set; } = true;
public bool ShowItemsRequiredForBundles { get; set; } = true;
public bool ShowHarvestPricesInShop { get; set; } = true;
public bool DisplayCalendarAndBillboard { get; set; } = true;
public bool ShowWhenNewRecipesAreAvailable { get; set; } = true;
public bool ShowToolUpgradeStatus { get; set; } = true;
public bool HideMerchantWhenVisited { get; set; } = false;
public bool ShowExactValue { get; set; } = false;
public bool ShowRobinBuildingStatusIcon { get; set; } = true;
public bool ShowSeasonalBerry { get; set; } = true;
public bool ShowSeasonalBerryHazelnut { get; set; } = false;
public bool ShowTodaysGifts { get; set; } = true;
public bool HideBirthdayIfFullFriendShip { get; set; } = true;
public Dictionary<string, bool> ShowLocationOfFriends { get; set; } = new();
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsCheckbox.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
using System;
namespace UIInfoSuite2.Options
{
internal class ModOptionsCheckbox : ModOptionsElement
{
private readonly Action<bool> _toggleOptionsDelegate;
private bool _isChecked;
private readonly Action<bool> _setOption;
private bool _canClick => !(_parent is ModOptionsCheckbox) || (_parent as ModOptionsCheckbox)._isChecked;
public ModOptionsCheckbox(
string label,
int whichOption,
Action<bool> toggleOptionDelegate,
Func<bool> getOption,
Action<bool> setOption,
ModOptionsCheckbox parent = null)
: base(label, whichOption, parent)
{
_toggleOptionsDelegate = toggleOptionDelegate;
_setOption = setOption;
_isChecked = getOption();
_toggleOptionsDelegate(_isChecked);
}
public override void ReceiveLeftClick(int x, int y)
{
if (_canClick)
{
Game1.playSound("drumkit6");
base.ReceiveLeftClick(x, y);
_isChecked = !_isChecked;
_setOption(_isChecked);
_toggleOptionsDelegate(_isChecked);
}
}
public override void Draw(SpriteBatch batch, int slotX, int slotY)
{
batch.Draw(Game1.mouseCursors, new Vector2(slotX + Bounds.X, slotY + Bounds.Y), new Rectangle?(_isChecked ? OptionsCheckbox.sourceRectChecked : OptionsCheckbox.sourceRectUnchecked), Color.White * (_canClick ? 1f : 0.33f), 0.0f, Vector2.Zero, Game1.pixelZoom, SpriteEffects.None, 0.4f);
base.Draw(batch, slotX, slotY);
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsElement.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StardewValley;
using StardewValley.BellsAndWhistles;
namespace UIInfoSuite2.Options
{
public class ModOptionsElement
{
protected const int DefaultX = 8;
protected const int DefaultY = 4;
protected const int DefaultPixelSize = 9;
private Rectangle _bounds;
private string _label;
private int _whichOption;
protected readonly ModOptionsElement _parent;
public Rectangle Bounds => _bounds;
public ModOptionsElement(string label, int whichOption = -1, ModOptionsElement parent = null)
{
int x = DefaultX * Game1.pixelZoom;
int y = DefaultY * Game1.pixelZoom;
int width = DefaultPixelSize * Game1.pixelZoom;
int height = DefaultPixelSize * Game1.pixelZoom;
if (parent != null)
x += DefaultX * 2 * Game1.pixelZoom;
_bounds = new Rectangle(x, y, width, height);
_label = label;
_whichOption = whichOption;
_parent = parent;
}
public virtual void ReceiveLeftClick(int x, int y)
{
}
public virtual void LeftClickHeld(int x, int y)
{
}
public virtual void LeftClickReleased(int x, int y)
{
}
public
virtual void ReceiveKeyPress(Keys key)
{
}
public virtual void Draw(SpriteBatch batch, int slotX, int slotY)
{
if (_whichOption < 0)
{
SpriteText.drawString(batch, _label, slotX + _bounds.X, slotY + _bounds.Y + Game1.pixelZoom * 3, 999, -1, 999, 1, 0.1f);
}
else
{
Utility.drawTextWithShadow(batch,
_label,
Game1.dialogueFont,
new Vector2(slotX + _bounds.X + _bounds.Width + Game1.pixelZoom * 2, slotY + _bounds.Y),
Game1.textColor,
1f,
0.1f);
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsPage.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
namespace UIInfoSuite2.Options
{
public class ModOptionsPage : IClickableMenu
{
private const int Width = 800;
private List<ClickableComponent> _optionSlots = new();
private List<ModOptionsElement> _options;
private string _hoverText;
private int _optionsSlotHeld;
private int _currentItemIndex;
private bool _isScrolling;
private ClickableTextureComponent _upArrow;
private ClickableTextureComponent _downArrow;
private ClickableTextureComponent _scrollBar;
private Rectangle _scrollBarRunner;
public ModOptionsPage(List<ModOptionsElement> options, IModEvents events)
: base(Game1.activeClickableMenu.xPositionOnScreen, Game1.activeClickableMenu.yPositionOnScreen + 10, Width, Game1.activeClickableMenu.height)
{
_options = options;
_upArrow = new ClickableTextureComponent(
new Rectangle(
xPositionOnScreen + width + Game1.tileSize / 4,
yPositionOnScreen + Game1.tileSize,
11 * Game1.pixelZoom,
12 * Game1.pixelZoom),
Game1.mouseCursors,
new Rectangle(421, 459, 11, 12),
Game1.pixelZoom);
_downArrow = new ClickableTextureComponent(
new Rectangle(
_upArrow.bounds.X,
yPositionOnScreen + height - Game1.tileSize,
_upArrow.bounds.Width,
_upArrow.bounds.Height),
Game1.mouseCursors,
new Rectangle(421, 472, 11, 12),
Game1.pixelZoom);
_scrollBar = new ClickableTextureComponent(
new Rectangle(
_upArrow.bounds.X + Game1.pixelZoom * 3,
_upArrow.bounds.Y + _upArrow.bounds.Height + Game1.pixelZoom,
6 * Game1.pixelZoom,
10 * Game1.pixelZoom),
Game1.mouseCursors,
new Rectangle(435, 463, 6, 10),
Game1.pixelZoom);
_scrollBarRunner = new Rectangle(_scrollBar.bounds.X,
_scrollBar.bounds.Y,
_scrollBar.bounds.Width,
height - Game1.tileSize * 2 - _upArrow.bounds.Height - Game1.pixelZoom * 2);
for (int i = 0; i < 7; ++i)
_optionSlots.Add(new ClickableComponent(
new Rectangle(
xPositionOnScreen + Game1.tileSize / 4,
yPositionOnScreen + Game1.tileSize * 5 / 4 + Game1.pixelZoom + i * (height - Game1.tileSize * 2) / 7,
width - Game1.tileSize / 2,
(height - Game1.tileSize * 2) / 7 + Game1.pixelZoom),
i.ToString()));
events.Display.MenuChanged += OnMenuChanged;
}
/// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
if (e.NewMenu is GameMenu)
{
xPositionOnScreen = Game1.activeClickableMenu.xPositionOnScreen;
yPositionOnScreen = Game1.activeClickableMenu.yPositionOnScreen + 10;
height = Game1.activeClickableMenu.height;
for (int i = 0; i < _optionSlots.Count; ++i)
{
var next = _optionSlots[i];
next.bounds.X = xPositionOnScreen + Game1.tileSize / 4;
next.bounds.Y = yPositionOnScreen + Game1.tileSize * 5 / 4 + Game1.pixelZoom + i * (height - Game1.tileSize * 2) / 7;
next.bounds.Width = width - Game1.tileSize / 2;
next.bounds.Height = (height - Game1.tileSize * 2) / 7 + Game1.pixelZoom;
}
_upArrow.bounds.X = xPositionOnScreen + width + Game1.tileSize / 4;
_upArrow.bounds.Y = yPositionOnScreen + Game1.tileSize;
_upArrow.bounds.Width = 11 * Game1.pixelZoom;
_upArrow.bounds.Height = 12 * Game1.pixelZoom;
_downArrow.bounds.X = _upArrow.bounds.X;
_downArrow.bounds.Y = yPositionOnScreen + height - Game1.tileSize;
_downArrow.bounds.Width = _upArrow.bounds.Width;
_downArrow.bounds.Height = _upArrow.bounds.Height;
_scrollBar.bounds.X = _upArrow.bounds.X + Game1.pixelZoom * 3;
_scrollBar.bounds.Y = _upArrow.bounds.Y + _upArrow.bounds.Height + Game1.pixelZoom;
_scrollBar.bounds.Width = 6 * Game1.pixelZoom;
_scrollBar.bounds.Height = 10 * Game1.pixelZoom;
_scrollBarRunner.X = _scrollBar.bounds.X;
_scrollBarRunner.Y = _scrollBar.bounds.Y;
_scrollBarRunner.Width = _scrollBar.bounds.Width;
_scrollBarRunner.Height = height - Game1.tileSize * 2 - _upArrow.bounds.Height - Game1.pixelZoom * 2;
}
}
private void SetScrollBarToCurrentItem()
{
if (_options.Count > 0)
{
_scrollBar.bounds.Y = _scrollBarRunner.Height / Math.Max(1, _options.Count - 7 + 1) * _currentItemIndex + _upArrow.bounds.Bottom + Game1.pixelZoom;
if (_currentItemIndex == _options.Count - 7)
{
_scrollBar.bounds.Y = _downArrow.bounds.Y - _scrollBar.bounds.Height - Game1.pixelZoom;
}
}
}
public override void leftClickHeld(int x, int y)
{
if (!GameMenu.forcePreventClose)
{
base.leftClickHeld(x, y);
if (_isScrolling)
{
int yBefore = _scrollBar.bounds.Y;
_scrollBar.bounds.Y = Math.Min(
yPositionOnScreen + height - Game1.tileSize - Game1.pixelZoom * 3 - _scrollBar.bounds.Height,
Math.Max(
y,
yPositionOnScreen + _upArrow.bounds.Height + Game1.pixelZoom * 5));
_currentItemIndex = Math.Min(
_options.Count - 7,
Math.Max(
0,
_options.Count * (y - _scrollBarRunner.Y) / _scrollBarRunner.Height));
SetScrollBarToCurrentItem();
if (yBefore != _scrollBar.bounds.Y)
Game1.playSound("shiny4");
}
else if (_optionsSlotHeld > -1 && _optionsSlotHeld + _currentItemIndex < _options.Count)
{
_options[_currentItemIndex + _optionsSlotHeld].LeftClickHeld(
x - _optionSlots[_optionsSlotHeld].bounds.X,
y - _optionSlots[_optionsSlotHeld].bounds.Y);
}
}
}
public override void receiveKeyPress(Keys key)
{
if (_optionsSlotHeld > -1 &&
_optionsSlotHeld + _currentItemIndex < _options.Count)
{
_options[_currentItemIndex + _optionsSlotHeld].ReceiveKeyPress(key);
}
}
public override void receiveScrollWheelAction(int direction)
{
if (!GameMenu.forcePreventClose)
{
base.receiveScrollWheelAction(direction);
if (direction > 0 && _currentItemIndex > 0)
{
UpArrowPressed();
Game1.playSound("shiny4");
}
else if (direction < 0 && _currentItemIndex < Math.Max(0, _options.Count - 7))
{
DownArrowPressed();
Game1.playSound("shiny4");
}
}
}
public override void releaseLeftClick(int x, int y)
{
if (!GameMenu.forcePreventClose)
{
base.releaseLeftClick(x, y);
if (_optionsSlotHeld > -1 && _optionsSlotHeld + _currentItemIndex < _options.Count)
{
ClickableComponent optionSlot = _optionSlots[_optionsSlotHeld];
_options[_currentItemIndex + _optionsSlotHeld].LeftClickReleased(x - optionSlot.bounds.X, y - optionSlot.bounds.Y);
}
_optionsSlotHeld = -1;
_isScrolling = false;
}
}
private void DownArrowPressed()
{
_downArrow.scale = _downArrow.baseScale;
++_currentItemIndex;
SetScrollBarToCurrentItem();
}
private void UpArrowPressed()
{
_upArrow.scale = _upArrow.baseScale;
--_currentItemIndex;
SetScrollBarToCurrentItem();
}
public override void receiveLeftClick(int x, int y, bool playSound = true)
{
if (!GameMenu.forcePreventClose)
{
if (_downArrow.containsPoint(x, y) && _currentItemIndex < Math.Max(0, _options.Count - 7))
{
DownArrowPressed();
Game1.playSound("shwip");
}
else if (_upArrow.containsPoint(x, y) && _currentItemIndex > 0)
{
UpArrowPressed();
Game1.playSound("shwip");
}
else if (_scrollBar.containsPoint(x, y))
{
_isScrolling = true;
}
else if (!_downArrow.containsPoint(x, y) &&
x > xPositionOnScreen + width &&
x < xPositionOnScreen + width + Game1.tileSize * 2 &&
y > yPositionOnScreen &&
y < yPositionOnScreen + height)
{
_isScrolling = true;
base.leftClickHeld(x, y);
base.releaseLeftClick(x, y);
}
_currentItemIndex = Math.Max(0, Math.Min(_options.Count - 7, _currentItemIndex));
for (int i = 0; i < _optionSlots.Count; ++i)
{
if (_optionSlots[i].bounds.Contains(x, y) &&
_currentItemIndex + i < _options.Count &&
_options[_currentItemIndex + i].Bounds.Contains(x - _optionSlots[i].bounds.X, y - _optionSlots[i].bounds.Y))
{
_options[_currentItemIndex + i].ReceiveLeftClick(
x - _optionSlots[i].bounds.X,
y - _optionSlots[i].bounds.Y);
_optionsSlotHeld = i;
break;
}
}
}
}
public override void receiveRightClick(int x, int y, bool playSound = true)
{
}
public override void receiveGamePadButton(Buttons b)
{
if (b == Buttons.A)
{
receiveLeftClick(Game1.getMouseX(), Game1.getMouseY());
}
}
public override void performHoverAction(int x, int y)
{
if (!GameMenu.forcePreventClose)
{
_hoverText = "";
_upArrow.tryHover(x, y);
_downArrow.tryHover(x, y);
_scrollBar.tryHover(x, y);
}
}
public override void draw(SpriteBatch batch)
{
Game1.drawDialogueBox(xPositionOnScreen, yPositionOnScreen - 10, width, height, false, true);
batch.End();
batch.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null);
for (int i = 0; i < _optionSlots.Count; ++i)
{
if (_currentItemIndex >= 0 &&
_currentItemIndex + i < _options.Count)
{
_options[_currentItemIndex + i].Draw(
batch,
_optionSlots[i].bounds.X,
_optionSlots[i].bounds.Y);
}
}
batch.End();
batch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
if (!GameMenu.forcePreventClose)
{
_upArrow.draw(batch);
_downArrow.draw(batch);
if (_options.Count > 7)
{
IClickableMenu.drawTextureBox(
batch,
Game1.mouseCursors,
new Rectangle(403, 383, 6, 6),
_scrollBarRunner.X,
_scrollBarRunner.Y,
_scrollBarRunner.Width,
_scrollBarRunner.Height,
Color.White,
Game1.pixelZoom,
false);
_scrollBar.draw(batch);
}
}
if (_hoverText != "")
IClickableMenu.drawHoverText(batch, _hoverText, Game1.smallFont);
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsPageButton.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
namespace UIInfoSuite2.Options
{
internal class ModOptionsPageButton : IClickableMenu
{
public Rectangle Bounds { get; }
//private readonly ModOptionsPageHandler _optionsPageHandler;
//private bool _hasClicked;
public event EventHandler OnLeftClicked;
public ModOptionsPageButton(IModEvents events)
{
//_optionsPageHandler = optionsPageHandler;
width = 64;
height = 64;
GameMenu activeClickableMenu = Game1.activeClickableMenu as GameMenu;
xPositionOnScreen = activeClickableMenu.xPositionOnScreen + activeClickableMenu.width - 200;
yPositionOnScreen = activeClickableMenu.yPositionOnScreen + 16;
Bounds = new Rectangle(xPositionOnScreen, yPositionOnScreen, width, height);
events.Input.ButtonPressed += OnButtonPressed;
events.Display.MenuChanged += OnMenuChanged;
}
/// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
if (e.NewMenu is GameMenu menu)
{
xPositionOnScreen = menu.xPositionOnScreen + menu.width - 200;
}
}
/// <summary>Raised after the player presses a button on the keyboard, controller, or mouse.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
public void OnButtonPressed(object sender, ButtonPressedEventArgs e)
{
if (e.Button == SButton.MouseLeft || e.Button == SButton.ControllerA)
{
var x = (int)Utility.ModifyCoordinateForUIScale(e.Cursor.ScreenPixels.X);
var y = (int)Utility.ModifyCoordinateForUIScale(e.Cursor.ScreenPixels.Y);
if (isWithinBounds(x, y))
{
receiveLeftClick(x, y);
OnLeftClicked?.Invoke(this, null);
}
}
}
public override void draw(SpriteBatch b)
{
base.draw(b);
Game1.spriteBatch.Draw(Game1.mouseCursors,
new Vector2(xPositionOnScreen, yPositionOnScreen),
new Rectangle(16, 368, 16, 16),
Color.White,
0.0f,
Vector2.Zero,
Game1.pixelZoom,
SpriteEffects.None,
1f);
b.Draw(Game1.mouseCursors,
new Vector2(xPositionOnScreen + 8, yPositionOnScreen + 14),
new Rectangle(32, 672, 16, 16),
Color.White,
0.0f,
Vector2.Zero,
3f,
SpriteEffects.None,
1f);
if (isWithinBounds(Game1.getMouseX(), Game1.getMouseY()))
{
IClickableMenu.drawHoverText(Game1.spriteBatch, "UI Info Mod Options", Game1.smallFont);
}
Tools.DrawMouseCursor();
}
public override void receiveRightClick(int x, int y, bool playSound = true)
{
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsPageHandler.cs
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using System.Reflection;
using UIInfoSuite2.Infrastructure.Extensions;
using UIInfoSuite2.UIElements;
namespace UIInfoSuite2.Options
{
internal class ModOptionsPageHandler : IDisposable
{
private readonly IModHelper _helper;
private readonly bool _showPersonalConfigButton;
private List<ModOptionsElement> _optionsElements = new();
private readonly List<IDisposable> _elementsToDispose;
private ModOptionsPage _modOptionsPage;
private ModOptionsPageButton _modOptionsPageButton;
private int _modOptionsTabPageNumber;
private PerScreen<IClickableMenu> _lastMenu = new();
private List<int> _instancesWithOptionsPageOpen = new();
private bool _windowResizing = false;
public ModOptionsPageHandler(IModHelper helper, ModOptions options, bool showPersonalConfigButton)
{
if (showPersonalConfigButton)
{
helper.Events.Display.RenderingActiveMenu += OnRenderingMenu;
helper.Events.Display.RenderedActiveMenu += OnRenderedMenu;
GameRunner.instance.Window.ClientSizeChanged += OnWindowClientSizeChanged;
helper.Events.Display.WindowResized += OnWindowResized;
}
_helper = helper;
_showPersonalConfigButton = showPersonalConfigButton;
var luckOfDay = new LuckOfDay(helper);
var showBirthdayIcon = new ShowBirthdayIcon(helper);
var showAccurateHearts = new ShowAccurateHearts(helper.Events);
var locationOfTownsfolk = new LocationOfTownsfolk(helper, options);
var showWhenAnimalNeedsPet = new ShowWhenAnimalNeedsPet(helper);
var showCalendarAndBillboardOnGameMenuButton = new ShowCalendarAndBillboardOnGameMenuButton(helper);
var showScarecrowAndSprinklerRange = new ShowItemEffectRanges(helper);
var experienceBar = new ExperienceBar(helper);
var showItemHoverInformation = new ShowItemHoverInformation(helper);
var shopHarvestPrices = new ShopHarvestPrices(helper);
var showQueenOfSauceIcon = new ShowQueenOfSauceIcon(helper);
var showTravelingMerchant = new ShowTravelingMerchant(helper);
var showRainyDayIcon = new ShowRainyDayIcon(helper);
var showCropAndBarrelTime = new ShowCropAndBarrelTime(helper);
var showToolUpgradeStatus = new ShowToolUpgradeStatus(helper);
var showRobinBuildingStatusIcon = new ShowRobinBuildingStatusIcon(helper);
var showSeasonalBerry = new ShowSeasonalBerry(helper);
var showTodaysGift = new ShowTodaysGifts(helper);
_elementsToDispose = new List<IDisposable>()
{
luckOfDay,
showBirthdayIcon,
showAccurateHearts,
locationOfTownsfolk,
showWhenAnimalNeedsPet,
showCalendarAndBillboardOnGameMenuButton,
showCropAndBarrelTime,
experienceBar,
showItemHoverInformation,
showTravelingMerchant,
showRainyDayIcon,
shopHarvestPrices,
showQueenOfSauceIcon,
showToolUpgradeStatus,
showRobinBuildingStatusIcon,
showSeasonalBerry
};
int whichOption = 1;
Version thisVersion = Assembly.GetAssembly(this.GetType()).GetName().Version;
_optionsElements.Add(new ModOptionsElement("UI Info Suite 2 v" + thisVersion.Major + "." + thisVersion.Minor + "." + thisVersion.Build));
var luckIcon = new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowLuckIcon)), whichOption++, luckOfDay.ToggleOption, () => options.ShowLuckIcon, v => options.ShowLuckIcon = v);
_optionsElements.Add(luckIcon);
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowExactValue)), whichOption++, luckOfDay.ToggleShowExactValueOption, () => options.ShowExactValue, v => options.ShowExactValue = v, luckIcon));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowLevelUpAnimation)), whichOption++, experienceBar.ToggleLevelUpAnimation, () => options.ShowLevelUpAnimation, v => options.ShowLevelUpAnimation = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowExperienceBar)), whichOption++, experienceBar.ToggleShowExperienceBar, () => options.ShowExperienceBar, v => options.ShowExperienceBar = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.AllowExperienceBarToFadeOut)), whichOption++, experienceBar.ToggleExperienceBarFade, () => options.AllowExperienceBarToFadeOut, v => options.AllowExperienceBarToFadeOut = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowExperienceGain)), whichOption++, experienceBar.ToggleShowExperienceGain, () => options.ShowExperienceGain, v => options.ShowExperienceGain = v));
if (!_helper.ModRegistry.IsLoaded("Bouhm.NPCMapLocations"))
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowLocationOfTownsPeople)), whichOption++, locationOfTownsfolk.ToggleShowNPCLocationsOnMap, () => options.ShowLocationOfTownsPeople, v => options.ShowLocationOfTownsPeople = v));
var birthdayIcon = new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowBirthdayIcon)), whichOption++, showBirthdayIcon.ToggleOption, () => options.ShowBirthdayIcon, v => options.ShowBirthdayIcon = v);
_optionsElements.Add(birthdayIcon);
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.HideBirthdayIfFullFriendShip)),
whichOption++, showBirthdayIcon.ToggleDisableOnMaxFriendshipOption, () => options.HideBirthdayIfFullFriendShip, v => options.HideBirthdayIfFullFriendShip = v, birthdayIcon));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowHeartFills)), whichOption++, showAccurateHearts.ToggleOption, () => options.ShowHeartFills, v => options.ShowHeartFills = v));
var animalPetIcon = new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowAnimalsNeedPets)), whichOption++, showWhenAnimalNeedsPet.ToggleOption, () => options.ShowAnimalsNeedPets, v => options.ShowAnimalsNeedPets = v);
_optionsElements.Add(animalPetIcon);
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.HideAnimalPetOnMaxFriendship)), whichOption++, showWhenAnimalNeedsPet.ToggleDisableOnMaxFriendshipOption, () => options.HideAnimalPetOnMaxFriendship, v => options.HideAnimalPetOnMaxFriendship = v, animalPetIcon));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.DisplayCalendarAndBillboard)), whichOption++, showCalendarAndBillboardOnGameMenuButton.ToggleOption, () => options.DisplayCalendarAndBillboard, v => options.DisplayCalendarAndBillboard = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowCropAndBarrelTooltip)), whichOption++, showCropAndBarrelTime.ToggleOption, () => options.ShowCropAndBarrelTooltip, v => options.ShowCropAndBarrelTooltip = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowItemEffectRanges)), whichOption++, showScarecrowAndSprinklerRange.ToggleOption, () => options.ShowItemEffectRanges, v => options.ShowItemEffectRanges = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowExtraItemInformation)), whichOption++, showItemHoverInformation.ToggleOption, () => options.ShowExtraItemInformation, v => options.ShowExtraItemInformation = v));
var travellingMerchantIcon = new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowTravelingMerchant)), whichOption++, showTravelingMerchant.ToggleOption, () => options.ShowTravelingMerchant, v => options.ShowTravelingMerchant = v);
_optionsElements.Add(travellingMerchantIcon);
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.HideMerchantWhenVisited)), whichOption++, showTravelingMerchant.ToggleHideWhenVisitedOption, () => options.HideMerchantWhenVisited, v => options.HideMerchantWhenVisited = v, travellingMerchantIcon));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowRainyDay)), whichOption++, showRainyDayIcon.ToggleOption, () => options.ShowRainyDay, v => options.ShowRainyDay = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowHarvestPricesInShop)), whichOption++, shopHarvestPrices.ToggleOption, () => options.ShowHarvestPricesInShop, v => options.ShowHarvestPricesInShop = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowWhenNewRecipesAreAvailable)), whichOption++, showQueenOfSauceIcon.ToggleOption, () => options.ShowWhenNewRecipesAreAvailable, v => options.ShowWhenNewRecipesAreAvailable = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowToolUpgradeStatus)), whichOption++, showToolUpgradeStatus.ToggleOption, () => options.ShowToolUpgradeStatus, v => options.ShowToolUpgradeStatus = v));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowRobinBuildingStatusIcon)), whichOption++, showRobinBuildingStatusIcon.ToggleOption, () => options.ShowRobinBuildingStatusIcon, v => options.ShowRobinBuildingStatusIcon = v));
var seasonalBerryIcon = new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowSeasonalBerry)), whichOption++, showSeasonalBerry.ToggleOption, () => options.ShowSeasonalBerry, v => options.ShowSeasonalBerry = v);
_optionsElements.Add(seasonalBerryIcon);
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowSeasonalBerryHazelnut)), whichOption++, showSeasonalBerry.ToggleHazelnutOption, () => options.ShowSeasonalBerryHazelnut, v => options.ShowSeasonalBerryHazelnut = v, seasonalBerryIcon));
_optionsElements.Add(new ModOptionsCheckbox(_helper.SafeGetString(nameof(options.ShowTodaysGifts)), whichOption++, showTodaysGift.ToggleOption, () => options.ShowTodaysGifts, v => options.ShowTodaysGifts = v));
}
public void Dispose()
{
foreach (var item in _elementsToDispose)
item.Dispose();
}
private void OnButtonLeftClicked(object sender, EventArgs e)
{
// Do not activate when an action is being remapped
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.readyToClose())
{
gameMenu.currentTab = _modOptionsTabPageNumber;
Game1.playSound("smallSelect");
}
}
// Early because it is called during Display.RenderingActiveMenu instead of later during Display.MenuChanged,
private void EarlyOnMenuChanged(IClickableMenu? oldMenu, IClickableMenu? newMenu)
{
if (_showPersonalConfigButton)
{
// Remove from old menu
if (oldMenu is GameMenu oldGameMenu)
{
if (_modOptionsPage != null)
{
oldGameMenu.pages.Remove(_modOptionsPage);
_modOptionsPage = null;
}
if (_modOptionsPageButton != null)
{
_modOptionsPageButton.OnLeftClicked -= OnButtonLeftClicked;
_modOptionsPageButton = null;
}
}
// Add to new menu
if (newMenu is GameMenu newGameMenu)
{
// Both modOptions variables require Game1.activeClickableMenu to not be null.
if (_modOptionsPage == null)
_modOptionsPage = new ModOptionsPage(_optionsElements, _helper.Events);
if (_modOptionsPageButton == null)
_modOptionsPageButton = new ModOptionsPageButton(_helper.Events);
_modOptionsPageButton.OnLeftClicked += OnButtonLeftClicked;
List<IClickableMenu> tabPages = newGameMenu.pages;
_modOptionsTabPageNumber = tabPages.Count;
tabPages.Add(_modOptionsPage);
}
}
}
private void OnRenderingMenu(object sender, RenderingActiveMenuEventArgs e)
{
if (_showPersonalConfigButton)
{
// Trigger the "EarlyOnMenuChanged" event
if (_lastMenu.Value != Game1.activeClickableMenu)
{
EarlyOnMenuChanged(_lastMenu.Value, Game1.activeClickableMenu);
_lastMenu.Value = Game1.activeClickableMenu;
}
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
// Draw our tab icon behind the menu even if it is dimmed by the menu's transparent background,
// so that it still displays during transitions eg. when a letter is viewed in the collections tab
DrawButton(gameMenu);
}
}
}
private void OnRenderedMenu(object sender, RenderedActiveMenuEventArgs e)
{
if (_showPersonalConfigButton
&& Game1.activeClickableMenu is GameMenu gameMenu
// But don't render when the map is displayed...
&& !(gameMenu.currentTab == GameMenu.mapTab
// ...or when a letter is opened in the collection's page
|| gameMenu.GetCurrentPage() is CollectionsPage cPage && cPage.letterviewerSubMenu != null
))
{
DrawButton(gameMenu);
// Draw the game menu's hover text again so it displays above our tab
if (!gameMenu.hoverText.Equals(""))
IClickableMenu.drawHoverText(Game1.spriteBatch, gameMenu.hoverText, Game1.smallFont);
}
}
private void OnWindowClientSizeChanged(object sender, EventArgs e)
{
if (_showPersonalConfigButton)
{
_windowResizing = true;
GameRunner.instance.ExecuteForInstances((Game1 instance) => {
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == _modOptionsTabPageNumber)
{
// Temporarily change all open mod options pages to the game's options page
// because the GameMenu is recreated when the window is resized, before we can add
// our mod options page to GameMenu#pages.
gameMenu.currentTab = GameMenu.optionsTab;
_instancesWithOptionsPageOpen.Add(instance.instanceId);
}
});
}
}
private void OnWindowResized(object sender, EventArgs e)
{
if (_windowResizing) {
_windowResizing = false;
GameRunner.instance.ExecuteForInstances((Game1 instance) => {
if (_instancesWithOptionsPageOpen.Remove(instance.instanceId))
{
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
gameMenu.currentTab = _modOptionsTabPageNumber;
}
}
});
}
}
private void DrawButton(GameMenu gameMenu)
{
_modOptionsPageButton.yPositionOnScreen = gameMenu.yPositionOnScreen + (gameMenu.currentTab == _modOptionsTabPageNumber ? 24 : 16);
_modOptionsPageButton.draw(Game1.spriteBatch);
}
}
}
UIInfoSuite2-master/UIInfoSuite2/Options/ModOptionsPageIcon.cs
using StardewValley.Menus;
namespace UIInfoSuite2.Options
{
internal class ModOptionsPageIcon : IClickableMenu
{
public override void receiveRightClick(int x, int y, bool playSound = true)
{
}
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ExperienceBar.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using StardewModdingAPI;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Tools;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UIInfoSuite2.Compatibility;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
using UIInfoSuite2.UIElements.ExperienceElements;
namespace UIInfoSuite2.UIElements
{
public partial class ExperienceBar : IDisposable
{
#region Properties
private readonly PerScreen<Item> _previousItem = new();
private readonly PerScreen<int[]> _currentExperience = new(createNewState: () => new int[5]);
private readonly PerScreen<int[]> _currentLevelExtenderExperience = new(createNewState: () => new int[5]);
private readonly PerScreen<int> _currentSkillLevel = new(createNewState: () => 0);
private readonly PerScreen<int> _experienceRequiredToLevel = new(createNewState: () => -1);
private readonly PerScreen<int> _experienceFromPreviousLevels = new(createNewState: () => -1);
private readonly PerScreen<int> _experienceEarnedThisLevel = new(createNewState: () => -1);
private readonly PerScreen<DisplayedExperienceBar> _displayedExperienceBar = new(createNewState: () => new DisplayedExperienceBar());
private readonly PerScreen<DisplayedLevelUpMessage> _displayedLevelUpMessage = new(createNewState: () => new DisplayedLevelUpMessage());
private readonly PerScreen<List<DisplayedExperienceValue>> _displayedExperienceValues = new(createNewState: () => new List<DisplayedExperienceValue>());
private const int LevelUpVisibleTicks = 120;
private readonly PerScreen<int> _levelUpVisibleTimer = new();
private const int ExperienceBarVisibleTicks = 480;
private readonly PerScreen<int> _experienceBarVisibleTimer = new();
private static readonly Dictionary<SkillType, Rectangle> SkillIconRectangles = new()
{
{ SkillType.Farming , new Rectangle(10, 428, 10, 10)},
{ SkillType.Fishing , new Rectangle(20, 428, 10, 10)},
{ SkillType.Foraging , new Rectangle(60, 428, 10, 10)},
{ SkillType.Mining , new Rectangle(30, 428, 10, 10)},
{ SkillType.Combat , new Rectangle(120, 428, 10, 10)}
};
private static readonly Dictionary<SkillType, Color> ExperienceFillColor = new()
{
{ SkillType.Farming , new Color(255, 251, 35, 0.38f)},
{ SkillType.Fishing , new Color(17, 84, 252, 0.63f)},
{ SkillType.Foraging , new Color(0, 234, 0, 0.63f)},
{ SkillType.Mining , new Color(145, 104, 63, 0.63f)},
{ SkillType.Combat , new Color(204, 0, 3, 0.63f)}
};
private readonly PerScreen<Rectangle> _experienceIconRectangle = new(createNewState: () => SkillIconRectangles[SkillType.Farming]);
private readonly PerScreen<Rectangle> _levelUpIconRectangle = new(createNewState: () => SkillIconRectangles[SkillType.Farming]);
private readonly PerScreen<Color> _experienceFillColor = new(createNewState: () => ExperienceFillColor[SkillType.Farming]);
private SoundEffectInstance _soundEffect;
private bool ExperienceBarFadeoutEnabled { get; set; } = true;
private bool ExperienceGainTextEnabled { get; set; } = true;
private bool LevelUpAnimationEnabled { get; set; } = true;
private bool ExperienceBarEnabled { get; set; } = true;
private readonly IModHelper _helper;
private readonly ILevelExtender _levelExtenderApi;
#endregion Properties
#region Lifecycle
public ExperienceBar(IModHelper helper)
{
_helper = helper;
InitializeSound();
if (_helper.ModRegistry.IsLoaded("DevinLematty.LevelExtender"))
{
_levelExtenderApi = _helper.ModRegistry.GetApi<ILevelExtender>("DevinLematty.LevelExtender");
}
}
private void InitializeSound()
{
string path = string.Empty;
try
{
path = Path.Combine(_helper.DirectoryPath, "assets", "LevelUp.wav");
_soundEffect = SoundEffect.FromStream(new FileStream(path, FileMode.Open)).CreateInstance();
}
catch (Exception ex)
{
ModEntry.MonitorObject.Log(
"Error loading sound file from " + path + ": " + ex.Message + Environment.NewLine + ex.StackTrace,
LogLevel.Error);
}
}
public void Dispose()
{
_soundEffect.Dispose();
}
public void ToggleOption(bool experienceBarEnabled, bool experienceBarFadeoutEnabled, bool experienceGainTextEnabled, bool levelUpAnimationEnabled)
{
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Player.Warped -= OnWarped;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked_HandleTimers;
_helper.Events.GameLoop.SaveLoaded -= OnSaveLoaded;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked_UpdateExperience;
_helper.Events.Player.LevelChanged -= OnLevelChanged;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked_UpdateExperience;
ExperienceBarEnabled = experienceBarEnabled;
ExperienceBarFadeoutEnabled = experienceBarFadeoutEnabled;
ExperienceGainTextEnabled = experienceGainTextEnabled;
LevelUpAnimationEnabled = levelUpAnimationEnabled;
if (ExperienceBarEnabled || ExperienceBarFadeoutEnabled || ExperienceGainTextEnabled || LevelUpAnimationEnabled)
{
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Player.Warped += OnWarped;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked_HandleTimers;
_helper.Events.GameLoop.SaveLoaded += OnSaveLoaded;
}
if (ExperienceBarEnabled || ExperienceGainTextEnabled)
{
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked_UpdateExperience;
}
if (LevelUpAnimationEnabled)
{
_helper.Events.Player.LevelChanged += OnLevelChanged;
}
}
public void ToggleShowExperienceBar(bool experienceBarEnabled)
{
ToggleOption(experienceBarEnabled, ExperienceBarFadeoutEnabled, ExperienceGainTextEnabled, LevelUpAnimationEnabled);
}
public void ToggleExperienceBarFade(bool experienceBarFadeoutEnabled)
{
ToggleOption(ExperienceBarEnabled, experienceBarFadeoutEnabled, ExperienceGainTextEnabled, LevelUpAnimationEnabled);
}
public void ToggleShowExperienceGain(bool experienceGainTextEnabled)
{
InitializeExperiencePoints();
ToggleOption(ExperienceBarEnabled, ExperienceBarFadeoutEnabled, experienceGainTextEnabled, LevelUpAnimationEnabled);
}
public void ToggleLevelUpAnimation(bool levelUpAnimationEnabled)
{
ToggleOption(ExperienceBarEnabled, ExperienceBarFadeoutEnabled, ExperienceGainTextEnabled, levelUpAnimationEnabled);
}
#endregion Lifecycle
#region Event subscriptions
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
InitializeExperiencePoints();
_displayedExperienceValues.Value.Clear();
}
private void OnLevelChanged(object sender, LevelChangedEventArgs e)
{
if (LevelUpAnimationEnabled && e.IsLocalPlayer)
{
_levelUpVisibleTimer.Value = LevelUpVisibleTicks;
_levelUpIconRectangle.Value = SkillIconRectangles[e.Skill];
_experienceBarVisibleTimer.Value = ExperienceBarVisibleTicks;
PlayLevelUpSoundEffect();
}
}
private void OnWarped(object sender, WarpedEventArgs e)
{
if (e.IsLocalPlayer)
_displayedExperienceValues.Value.Clear();
}
private void OnUpdateTicked_UpdateExperience(object sender, UpdateTickedEventArgs e)
{
if (!e.IsMultipleOf(15)) // quarter second
return;
bool skillChanged = TryGetCurrentLevelIndexFromSkillChange(out int currentLevelIndex);
bool itemChanged = Game1.player.CurrentItem != _previousItem.Value;
if (itemChanged)
{
currentLevelIndex = GetCurrentLevelIndexFromItemChange(Game1.player.CurrentItem);
_previousItem.Value = Game1.player.CurrentItem;
}
if (skillChanged || itemChanged)
{
UpdateExperience(currentLevelIndex, skillChanged);
}
}
public void OnUpdateTicked_HandleTimers(object sender, UpdateTickedEventArgs e)
{
if (_levelUpVisibleTimer.Value > 0)
{
_levelUpVisibleTimer.Value--;
}
if (_experienceBarVisibleTimer.Value > 0)
{
_experienceBarVisibleTimer.Value--;
}
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
if (Game1.eventUp)
return;
// Level up text
if (LevelUpAnimationEnabled && _levelUpVisibleTimer.Value != 0)
{
_displayedLevelUpMessage.Value.Draw(_levelUpIconRectangle.Value, _helper.SafeGetString(LanguageKeys.LevelUp));
}
// Experience values
for (int i = _displayedExperienceValues.Value.Count - 1; i >= 0; --i)
{
if (_displayedExperienceValues.Value[i].IsInvisible)
{
_displayedExperienceValues.Value.RemoveAt(i);
}
else
{
if (ExperienceGainTextEnabled)
_displayedExperienceValues.Value[i].Draw();
}
}
// Experience bar
if (ExperienceBarEnabled && (_experienceBarVisibleTimer.Value != 0 || !ExperienceBarFadeoutEnabled) && _experienceRequiredToLevel.Value > 0)
{
_displayedExperienceBar.Value.Draw(_experienceFillColor.Value, _experienceIconRectangle.Value,
_experienceEarnedThisLevel.Value, _experienceRequiredToLevel.Value - _experienceFromPreviousLevels.Value, _currentSkillLevel.Value);
}
}
#endregion Event subscriptions
#region Logic
private void InitializeExperiencePoints()
{
for (var i = 0; i < _currentExperience.Value.Length; ++i)
_currentExperience.Value[i] = Game1.player.experiencePoints[i];
if (_levelExtenderApi != null)
{
for (var i = 0; i < _currentLevelExtenderExperience.Value.Length; ++i)
_currentLevelExtenderExperience.Value[i] = _levelExtenderApi.CurrentXP()[i];
}
}
private void PlayLevelUpSoundEffect()
{
if (_soundEffect == null)
return;
_soundEffect.Volume = Game1.options.soundVolumeLevel;
Task.Factory.StartNew(async () =>
{
await Task.Delay(200);
_soundEffect?.Play();
});
}
private bool TryGetCurrentLevelIndexFromSkillChange(out int currentLevelIndex)
{
currentLevelIndex = -1;
for (var i = 0; i < _currentExperience.Value.Length; ++i)
{
if (_currentExperience.Value[i] != Game1.player.experiencePoints[i] ||
(_levelExtenderApi != null && _currentLevelExtenderExperience.Value[i] != _levelExtenderApi.CurrentXP()[i]))
{
currentLevelIndex = i;
break;
}
}
return currentLevelIndex != -1;
}
private static int GetCurrentLevelIndexFromItemChange(Item currentItem)
{
return currentItem switch
{
FishingRod => (int)SkillType.Fishing,
Pickaxe => (int)SkillType.Mining,
MeleeWeapon weapon when weapon.Name != "Scythe" => (int)SkillType.Combat,
_ when Game1.currentLocation is Farm && currentItem is not Axe => (int)SkillType.Farming,
_ => (int)SkillType.Foraging
};
}
private void UpdateExperience(int currentLevelIndex, bool displayExperience)
{
_experienceBarVisibleTimer.Value = ExperienceBarVisibleTicks;
_experienceIconRectangle.Value = SkillIconRectangles[(SkillType)currentLevelIndex];
_experienceFillColor.Value = ExperienceFillColor[(SkillType)currentLevelIndex];
_currentSkillLevel.Value = Game1.player.GetSkillLevel(currentLevelIndex);
_experienceRequiredToLevel.Value = GetExperienceRequiredToLevel(_currentSkillLevel.Value);
_experienceFromPreviousLevels.Value
= GetExperienceRequiredToLevel(_currentSkillLevel.Value - 1);
_experienceEarnedThisLevel.Value = Game1.player.experiencePoints[currentLevelIndex] - _experienceFromPreviousLevels.Value;
if (_experienceRequiredToLevel.Value <= 0 && _levelExtenderApi != null)
{
_experienceEarnedThisLevel.Value = _levelExtenderApi.CurrentXP()[currentLevelIndex];
_experienceFromPreviousLevels.Value = _currentExperience.Value[currentLevelIndex] - _experienceEarnedThisLevel.Value;
_experienceRequiredToLevel.Value = _levelExtenderApi.RequiredXP()[currentLevelIndex] + _experienceFromPreviousLevels.Value;
}
if (displayExperience)
{
if (ExperienceGainTextEnabled && _experienceRequiredToLevel.Value > 0)
{
int currentExperienceToUse = Game1.player.experiencePoints[currentLevelIndex];
var previousExperienceToUse = _currentExperience.Value[currentLevelIndex];
if (_levelExtenderApi != null && _currentSkillLevel.Value > 9)
{
currentExperienceToUse = _levelExtenderApi.CurrentXP()[currentLevelIndex];
previousExperienceToUse = _currentLevelExtenderExperience.Value[currentLevelIndex];
}
int experienceGain = currentExperienceToUse - previousExperienceToUse;
if (experienceGain > 0)
{
_displayedExperienceValues.Value.Add(
new DisplayedExperienceValue(
experienceGain,
Game1.player.getLocalPosition(Game1.viewport)));
}
}
_currentExperience.Value[currentLevelIndex] = Game1.player.experiencePoints[currentLevelIndex];
if (_levelExtenderApi != null)
_currentLevelExtenderExperience.Value[currentLevelIndex] = _levelExtenderApi.CurrentXP()[currentLevelIndex];
}
}
private static int GetExperienceRequiredToLevel(int currentLevel) => currentLevel switch
{
0 => 100,
1 => 380,
2 => 770,
3 => 1300,
4 => 2150,
5 => 3300,
6 => 4800,
7 => 6900,
8 => 10000,
9 => 15000,
_ => -1
};
#endregion Logic
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ExperienceElements/DisplayedExperienceBar.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
using System;
namespace UIInfoSuite2.UIElements.ExperienceElements
{
public class DisplayedExperienceBar
{
private const int MaxBarWidth = 175;
public void Draw(Color experienceFillColor, Rectangle experienceIconPosition,
int experienceEarnedThisLevel, int experienceDifferenceBetweenLevels, int currentLevel)
{
int barWidth = GetBarWidth(experienceEarnedThisLevel, experienceDifferenceBetweenLevels);
float leftSide = GetExperienceBarLeftSide();
Game1.drawDialogueBox(
(int)leftSide,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 160,
240,
160,
false,
true);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
barWidth,
31), experienceFillColor);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
Math.Min(4, barWidth),
31), experienceFillColor);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 63,
barWidth,
4), experienceFillColor);
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
(int)leftSide + 32,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 36,
barWidth,
4), experienceFillColor);
if (IsMouseOverExperienceBar(leftSide))
{
Game1.drawWithBorder(
experienceEarnedThisLevel + "/" + experienceDifferenceBetweenLevels,
Color.Black,
Color.Black,
new Vector2(
leftSide + 33,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 70));
}
else
{
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(
leftSide + 54,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 62), experienceIconPosition, Color.White,
0,
Vector2.Zero,
2.9f,
SpriteEffects.None,
0.85f);
Game1.drawWithBorder(
currentLevel.ToString(),
Color.Black * 0.6f,
Color.Black,
new Vector2(
leftSide + 33,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 70));
}
}
#region Static helpers
private static int GetBarWidth(int experienceEarnedThisLevel, int experienceDifferenceBetweenLevels) =>
(int)((double)experienceEarnedThisLevel / experienceDifferenceBetweenLevels * MaxBarWidth);
private static float GetExperienceBarLeftSide()
{
float leftSide = Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Left;
if (Game1.isOutdoorMapSmallerThanViewport())
{
int num3 = Game1.currentLocation.map.Layers[0].LayerWidth * Game1.tileSize;
leftSide += (Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Right - num3) / 2;
}
return leftSide;
}
private static bool IsMouseOverExperienceBar(float leftSide)
{
return GetExperienceBarTextureComponent(leftSide).containsPoint(Game1.getMouseX(), Game1.getMouseY());
}
private static ClickableTextureComponent GetExperienceBarTextureComponent(float leftSide)
{
return new ClickableTextureComponent(
"",
new Rectangle(
(int)leftSide - 36,
Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - 80,
260,
100),
"",
"",
Game1.mouseCursors,
new Rectangle(0, 0, 0, 0),
Game1.pixelZoom);
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ExperienceElements/DisplayedExperienceValue.cs
using Microsoft.Xna.Framework;
using StardewValley;
namespace UIInfoSuite2.UIElements.ExperienceElements
{
internal class DisplayedExperienceValue
{
private readonly float _experiencePoints;
private Vector2 _position;
private int _alpha = 100;
public DisplayedExperienceValue(float experiencePoints, Vector2 position)
{
_experiencePoints = experiencePoints;
_position = position;
}
public void Draw()
{
_position.Y -= 0.5f;
--_alpha;
Game1.drawWithBorder(
"Exp " + _experiencePoints,
Color.DarkSlateGray * (_alpha / 100f),
Color.PaleTurquoise * (_alpha / 100f),
Utility.ModifyCoordinatesForUIScale(new Vector2(_position.X - 28, _position.Y - 130)),
0.0f,
0.8f,
0.0f);
}
public bool IsInvisible => _alpha < 3;
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ExperienceElements/DisplayedLevelUpMessage.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
namespace UIInfoSuite2.UIElements.ExperienceElements
{
public class DisplayedLevelUpMessage
{
public void Draw(Rectangle levelUpIconRectangle, string levelUpMessage)
{
Vector2 playerLocalPosition = Game1.player.getLocalPosition(Game1.viewport);
Game1.spriteBatch.Draw(
Game1.mouseCursors,
Utility.ModifyCoordinatesForUIScale(new Vector2(
playerLocalPosition.X - 74,
playerLocalPosition.Y - 130)), levelUpIconRectangle,
Color.White,
0,
Vector2.Zero,
Game1.pixelZoom,
SpriteEffects.None,
0.85f);
Game1.drawWithBorder(
levelUpMessage,
Color.DarkSlateGray,
Color.PaleTurquoise,
Utility.ModifyCoordinatesForUIScale(new Vector2(
playerLocalPosition.X - 28,
playerLocalPosition.Y - 130)));
}
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/LocationOfTownsfolk.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Locations;
using StardewValley.Menus;
using StardewValley.Quests;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
using UIInfoSuite2.Options;
namespace UIInfoSuite2.UIElements
{
internal class LocationOfTownsfolk : IDisposable
{
#region Properties
private SocialPage _socialPage;
private string[] _friendNames;
private List<NPC> _townsfolk = new();
private List<OptionsCheckbox> _checkboxes = new();
private readonly ModOptions _options;
private readonly IModHelper _helper;
private const int SocialPanelWidth = 190;
private const int SocialPanelXOffset = 160;
private static readonly Dictionary<string, KeyValuePair<int, int>> _mapLocations = new()
{
{ "HarveyRoom", new KeyValuePair<int, int>(677, 304) },
{ "BathHouse_Pool", new KeyValuePair<int, int>(576, 60) },
{ "WizardHouseBasement", new KeyValuePair<int, int>(196, 352) },
{ "BugLand", new KeyValuePair<int, int>(0, 0) },
{ "Desert", new KeyValuePair<int, int>(75, 40) },
{ "Cellar", new KeyValuePair<int, int>(470, 260) },
{ "JojaMart", new KeyValuePair<int, int>(872, 280) },
{ "LeoTreeHouse", new KeyValuePair<int, int>(744, 128) },
{ "Tent", new KeyValuePair<int, int>(784, 128) },
{ "HaleyHouse", new KeyValuePair<int, int>(652, 408) },
{ "Hospital", new KeyValuePair<int, int>(677, 304) },
{ "FarmHouse", new KeyValuePair<int, int>(470, 260) },
{ "Farm", new KeyValuePair<int, int>(470, 260) },
{ "ScienceHouse", new KeyValuePair<int, int>(732, 148) },
{ "ManorHouse", new KeyValuePair<int, int>(768, 395) },
{ "AdventureGuild", new KeyValuePair<int, int>(0, 0) },
{ "SeedShop", new KeyValuePair<int, int>(696, 296) },
{ "Blacksmith", new KeyValuePair<int, int>(852, 388) },
{ "JoshHouse", new KeyValuePair<int, int>(740, 320) },
{ "SandyHouse", new KeyValuePair<int, int>(40, 115) },
{ "Tunnel", new KeyValuePair<int, int>(0, 0) },
{ "CommunityCenter", new KeyValuePair<int, int>(692, 204) },
{ "Backwoods", new KeyValuePair<int, int>(460, 156) },
{ "ElliottHouse", new KeyValuePair<int, int>(826, 550) },
{ "SebastianRoom", new KeyValuePair<int, int>(732, 148) },
{ "BathHouse_Entry", new KeyValuePair<int, int>(576, 60) },
{ "Greenhouse", new KeyValuePair<int, int>(370, 270) },
{ "Sewer", new KeyValuePair<int, int>(380, 596) },
{ "WizardHouse", new KeyValuePair<int, int>(196, 352) },
{ "Trailer", new KeyValuePair<int, int>(780, 360) },
{ "Trailer_Big", new KeyValuePair<int, int>(780, 360) },
{ "Forest", new KeyValuePair<int, int>(80, 272) },
{ "Woods", new KeyValuePair<int, int>(100, 272) },
{ "WitchSwamp", new KeyValuePair<int, int>(0, 0) },
{ "ArchaeologyHouse", new KeyValuePair<int, int>(892, 416) },
{ "FishShop", new KeyValuePair<int, int>(844, 608) },
{ "Saloon", new KeyValuePair<int, int>(714, 354) },
{ "LeahHouse", new KeyValuePair<int, int>(452, 436) },
{ "Town", new KeyValuePair<int, int>(680, 360) },
{ "Mountain", new KeyValuePair<int, int>(762, 154) },
{ "BusStop", new KeyValuePair<int, int>(516, 224) },
{ "Railroad", new KeyValuePair<int, int>(644, 64) },
{ "SkullCave", new KeyValuePair<int, int>(0, 0) },
{ "BathHouse_WomensLocker", new KeyValuePair<int, int>(576, 60) },
{ "Beach", new KeyValuePair<int, int>(790, 550) },
{ "BathHouse_MensLocker", new KeyValuePair<int, int>(576, 60) },
{ "Mine", new KeyValuePair<int, int>(880, 100) },
{ "WitchHut", new KeyValuePair<int, int>(0, 0) },
{ "AnimalShop", new KeyValuePair<int, int>(420, 392) },
{ "SamHouse", new KeyValuePair<int, int>(612, 396) },
{ "WitchWarpCave", new KeyValuePair<int, int>(0, 0) },
{ "Club", new KeyValuePair<int, int>(60, 92) },
{ "Sunroom", new KeyValuePair<int, int>(705, 304) }
};
#endregion
#region Lifecycle
public LocationOfTownsfolk(IModHelper helper, ModOptions options)
{
_helper = helper;
_options = options;
}
public void ToggleShowNPCLocationsOnMap(bool showLocations)
{
InitializeProperties();
_helper.Events.Display.MenuChanged -= OnMenuChanged;
_helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu_DrawSocialPageOptions;
_helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu_DrawNPCLocationsOnMap;
_helper.Events.Input.ButtonPressed -= OnButtonPressed_ForSocialPage;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showLocations)
{
_helper.Events.Display.MenuChanged += OnMenuChanged;
_helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu_DrawSocialPageOptions;
_helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu_DrawNPCLocationsOnMap;
_helper.Events.Input.ButtonPressed += OnButtonPressed_ForSocialPage;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
public void Dispose()
{
ToggleShowNPCLocationsOnMap(false);
}
#endregion
#region Event subscriptions
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
InitializeProperties();
}
private void OnButtonPressed_ForSocialPage(object sender, ButtonPressedEventArgs e)
{
if (Game1.activeClickableMenu is GameMenu && (e.Button == SButton.MouseLeft || e.Button == SButton.ControllerA || e.Button == SButton.ControllerX))
{
CheckSelectedBox(e);
}
}
private void OnRenderedActiveMenu_DrawSocialPageOptions(object sender, RenderedActiveMenuEventArgs e)
{
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 2)
{
DrawSocialPageOptions();
}
}
private void OnRenderedActiveMenu_DrawNPCLocationsOnMap(object sender, RenderedActiveMenuEventArgs e)
{
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 3)
{
DrawNPCLocationsOnMap(gameMenu);
}
}
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (!e.IsOneSecond || (Context.IsSplitScreen && Context.ScreenId != 0))
return;
_townsfolk.Clear();
foreach (var loc in Game1.locations)
{
foreach (var character in loc.characters)
{
if (character.isVillager())
_townsfolk.Add(character);
}
}
}
#endregion
#region Logic
private void InitializeProperties()
{
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
foreach (var menu in gameMenu.pages)
{
if (menu is SocialPage socialPage)
{
_socialPage = socialPage;
_friendNames = _socialPage.names
.Select(name => name.ToString())
.ToArray();
break;
}
}
_checkboxes.Clear();
for (int i = 0; i < _friendNames.Length; i++)
{
var friendName = _friendNames[i];
OptionsCheckbox checkbox = new OptionsCheckbox("", i);
if (Game1.player.friendshipData.ContainsKey(friendName))
{
// npc
checkbox.greyedOut = false;
checkbox.isChecked = _options.ShowLocationOfFriends.SafeGet(friendName, true);
}
else
{
// player
checkbox.greyedOut = true;
checkbox.isChecked = true;
}
_checkboxes.Add(checkbox);
}
}
}
private void CheckSelectedBox(ButtonPressedEventArgs e)
{
int slotPosition = (int)typeof(SocialPage)
.GetField("slotPosition", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(_socialPage);
for (int i = slotPosition; i < slotPosition + 5; ++i)
{
OptionsCheckbox checkbox = _checkboxes[i];
var rect = new Rectangle(checkbox.bounds.X, checkbox.bounds.Y, checkbox.bounds.Width, checkbox.bounds.Height);
if (e.Button == SButton.ControllerX)
{
rect.Width += SocialPanelWidth + Game1.activeClickableMenu.width;
}
if (rect.Contains((int)Utility.ModifyCoordinateForUIScale(Game1.getMouseX()), (int)Utility.ModifyCoordinateForUIScale(Game1.getMouseY())) &&
!checkbox.greyedOut)
{
checkbox.isChecked = !checkbox.isChecked;
_options.ShowLocationOfFriends[_friendNames[checkbox.whichOption]] = checkbox.isChecked;
Game1.playSound("drumkit6");
}
}
}
private void DrawSocialPageOptions()
{
Game1.drawDialogueBox(Game1.activeClickableMenu.xPositionOnScreen - SocialPanelXOffset, Game1.activeClickableMenu.yPositionOnScreen,
SocialPanelWidth, Game1.activeClickableMenu.height, false, true);
int slotPosition = (int)typeof(SocialPage)
.GetField("slotPosition", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(_socialPage);
int yOffset = 0;
for (int i = slotPosition; i < slotPosition + 5 && i < _friendNames.Length; ++i)
{
OptionsCheckbox checkbox = _checkboxes[i];
checkbox.bounds.X = Game1.activeClickableMenu.xPositionOnScreen - 60;
checkbox.bounds.Y = Game1.activeClickableMenu.yPositionOnScreen + 130 + yOffset;
checkbox.draw(Game1.spriteBatch, 0, 0);
yOffset += 112;
Color color = checkbox.isChecked ? Color.White : Color.Gray;
Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2(checkbox.bounds.X - 50, checkbox.bounds.Y), new Rectangle(80, 0, 16, 16),
color, 0.0f, Vector2.Zero, 3f, SpriteEffects.None, 1f);
if (yOffset != 560)
{
// draw seperator line
Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(checkbox.bounds.X - 50, checkbox.bounds.Y + 72, SocialPanelWidth / 2 - 6, 4), Color.SaddleBrown);
Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(checkbox.bounds.X - 50, checkbox.bounds.Y + 76, SocialPanelWidth / 2 - 6, 4), Color.BurlyWood);
}
if (!Game1.options.hardwareCursor)
{
Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2(Game1.getMouseX(), Game1.getMouseY()),
Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, Game1.mouseCursor, 16, 16),
Color.White, 0.0f, Vector2.Zero, Game1.pixelZoom + (Game1.dialogueButtonScale / 150.0f), SpriteEffects.None, 1f);
}
if (checkbox.bounds.Contains(Game1.getMouseX(), Game1.getMouseY()))
IClickableMenu.drawHoverText(Game1.spriteBatch, "Track on map", Game1.dialogueFont);
}
}
private void DrawNPCLocationsOnMap(GameMenu gameMenu)
{
List<string> namesToShow = new List<string>();
foreach (var character in _townsfolk)
{
try
{
bool shouldDrawCharacter = Game1.player.friendshipData.ContainsKey(character.Name) && _options.ShowLocationOfFriends.SafeGet(character.Name, true) && character.id != -1;
if (shouldDrawCharacter)
{
DrawNPC(character, namesToShow);
}
}
catch (Exception ex)
{
ModEntry.MonitorObject.Log(ex.Message + Environment.NewLine + ex.StackTrace, LogLevel.Error);
}
}
DrawNPCNames(namesToShow);
//The cursor needs to show up in front of the character faces
Tools.DrawMouseCursor();
string hoverText = (string)typeof(MapPage)
.GetField("hoverText", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(gameMenu.pages[gameMenu.currentTab]);
IClickableMenu.drawHoverText(Game1.spriteBatch, hoverText, Game1.smallFont);
}
private static void DrawNPC(NPC character, List<string> namesToShow)
{
KeyValuePair<int, int> location = GetMapCoordinatesForNPC(character);
Rectangle headShot = character.GetHeadShot();
int xBase = Game1.activeClickableMenu.xPositionOnScreen - 158;
int yBase = Game1.activeClickableMenu.yPositionOnScreen - 40;
int x = xBase + location.Key;
int y = yBase + location.Value;
Color
color = character.CurrentDialogue.Count <= 0 ? Color.Gray : Color.White;
ClickableTextureComponent textureComponent = new ClickableTextureComponent(character.Name, new Rectangle(x, y, 0, 0),
null, character.Name, character.Sprite.Texture, headShot, 2.3f);
float headShotScale = 2f;
Game1.spriteBatch.Draw(character.Sprite.Texture, new Vector2(x, y), new Rectangle?(headShot),
color, 0.0f, Vector2.Zero, headShotScale, SpriteEffects.None, 1f);
int mouseX = Game1.getMouseX();
int mouseY = Game1.getMouseY();
if (mouseX >= x && mouseX <= x + headShot.Width * headShotScale && mouseY >= y && mouseY <= y + headShot.Height * headShotScale)
{
namesToShow.Add(character.displayName);
}
DrawQuestsForNPC(character, x, y);
}
private static KeyValuePair<int, int> GetMapCoordinatesForNPC(NPC character)
{
string locationName = character.currentLocation?.Name ?? character.DefaultMap;
// Ginger Island
if (character.currentLocation is IslandLocation)
{
return new KeyValuePair<int, int>(1104, 658);
}
// Scale Town and Forest
if (locationName == "Town" || locationName == "Forest")
{
int xStart = locationName == "Town" ? 595 : 183;
int yStart = locationName == "Town" ? 163 : 378;
int areaWidth = locationName == "Town" ? 345 : 319;
int areaHeight = locationName == "Town" ? 330 : 261;
xTile.Map map = character.currentLocation.Map;
float xScale = areaWidth / (float)map.DisplayWidth;
float yScale = areaHeight / (float)map.DisplayHeight;
float scaledX = character.position.X * xScale;
float scaledY = character.position.Y * yScale;
int xPos = (int)scaledX + xStart;
int yPos = (int)scaledY + yStart;
return new KeyValuePair<int, int>(xPos, yPos);
}
// Other known locations
return _mapLocations.SafeGet(locationName, new KeyValuePair<int, int>(0, 0));
}
private static void DrawQuestsForNPC(NPC character, int x, int y)
{
foreach (var quest in Game1.player.questLog.Where(q => q.accepted.Value && q.dailyQuest.Value && !q.completed.Value))
{
bool isQuestTarget = false;
switch (quest.questType.Value)
{
case 3: isQuestTarget = (quest as ItemDeliveryQuest).target.Value == character.Name; break;
case 4: isQuestTarget = (quest as SlayMonsterQuest).target.Value == character.Name; break;
case 7: isQuestTarget = (quest as FishingQuest).target.Value == character.Name; break;
case 10: isQuestTarget = (quest as ResourceCollectionQuest).target.Value == character.Name; break;
}
if (isQuestTarget)
Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2(x + 10, y - 12), new Rectangle(394, 495, 4, 10),
Color.White, 0.0f, Vector2.Zero, 3f, SpriteEffects.None, 1f);
}
}
private static void DrawNPCNames(List<string> namesToShow)
{
if (namesToShow.Count == 0)
return;
StringBuilder text = new StringBuilder();
int longestLength = 0;
foreach (string name in namesToShow)
{
text.AppendLine(name);
longestLength = Math.Max(longestLength, (int)Math.Ceiling(Game1.smallFont.MeasureString(name).Length()));
}
int windowHeight = Game1.smallFont.LineSpacing * namesToShow.Count + 25;
Vector2 windowPos = new Vector2(Game1.getMouseX() + 40, Game1.getMouseY() - windowHeight);
IClickableMenu.drawTextureBox(Game1.spriteBatch, (int)windowPos.X, (int)windowPos.Y,
longestLength + 30, Game1.smallFont.LineSpacing * namesToShow.Count + 25, Color.White);
Game1.spriteBatch.DrawString(Game1.smallFont, text, new Vector2(windowPos.X + 17, windowPos.Y + 17), Game1.textShadowColor);
Game1.spriteBatch.DrawString(Game1.smallFont, text, new Vector2(windowPos.X + 15, windowPos.Y + 15), Game1.textColor);
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/LuckOfDay.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class LuckOfDay : IDisposable
{
#region Properties
private readonly PerScreen<string> _hoverText = new(createNewState: () => string.Empty);
private readonly PerScreen<Color> _color = new(createNewState: () => new Color(Color.White.ToVector4()));
private readonly PerScreen<ClickableTextureComponent> _icon = new(createNewState: () => new ClickableTextureComponent("",
new Rectangle(Tools.GetWidthInPlayArea() - 134,
290,
10 * Game1.pixelZoom,
10 * Game1.pixelZoom),
"",
"",
Game1.mouseCursors,
new Rectangle(50, 428, 10, 14),
Game1.pixelZoom,
false));
private readonly IModHelper _helper;
private bool Enabled { get; set; }
private bool ShowExactValue { get; set; }
private static readonly Color Luck1Color = new(87, 255, 106, 255);
private static readonly Color Luck2Color = new(148, 255, 210, 255);
private static readonly Color Luck3Color = new(246, 255, 145, 255);
private static readonly Color Luck4Color = new(255, 255, 255, 255);
private static readonly Color Luck5Color = new(255, 155, 155, 255);
private static readonly Color Luck6Color = new(165, 165, 165, 204);
#endregion
#region Lifecycle
public LuckOfDay(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showLuckOfDay)
{
Enabled = showLuckOfDay;
_helper.Events.Player.Warped -= OnWarped;
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showLuckOfDay)
{
AdjustIconXToBlackBorder();
_helper.Events.Player.Warped += OnWarped;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
}
}
public void ToggleShowExactValueOption(bool showExactValue)
{
ShowExactValue = showExactValue;
ToggleOption(Enabled);
}
#endregion
#region Event subscriptions
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
CalculateLuck(e);
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// draw hover text
if (_icon.Value.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
IClickableMenu.drawHoverText(Game1.spriteBatch, _hoverText.Value, Game1.dialogueFont);
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
// draw dice icon
if (!Game1.eventUp)
{
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
var icon = _icon.Value;
icon.bounds.X = iconPosition.X;
icon.bounds.Y = iconPosition.Y;
_icon.Value = icon;
_icon.Value.draw(Game1.spriteBatch, _color.Value, 1f);
}
}
#endregion
#region Logic
private void CalculateLuck(UpdateTickedEventArgs e)
{
if (e.IsMultipleOf(30)) // half second
{
switch (Game1.player.DailyLuck)
{
// Spirits are very happy (FeelingLucky)
case var l when (l > 0.07):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus1);
_color.Value = Luck1Color;
break;
// Spirits are in good humor (LuckyButNotTooLucky)
case var l when (l > 0.02 && l <= 0.07):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus2);
_color.Value = Luck2Color;
break;
// The spirits feel neutral
case var l when (l >= -0.02 && l <= 0.02 && l != 0):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus3);
_color.Value = Luck3Color;
break;
// The spirits feel absolutely neutral
case var l when (l == 0):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus4);
_color.Value = Luck4Color;
break;
// The spirits are somewhat annoyed (NotFeelingLuckyAtAll)
case var l when (l >= -0.07 && l < -0.02):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus5);
_color.Value = Luck5Color;
break;
// The spirits are very displeased (MaybeStayHome)
case var l when (l < -0.07):
_hoverText.Value = _helper.SafeGetString(LanguageKeys.LuckStatus6);
_color.Value = Luck6Color;
break;
}
// Rewrite the text, but keep the color
if (ShowExactValue)
{
_hoverText.Value = string.Format(_helper.SafeGetString(LanguageKeys.DailyLuckValue), Game1.player.DailyLuck.ToString("N3"));
}
}
}
private void OnWarped(object sender, WarpedEventArgs e)
{
// adjust icon X to black border
if (e.IsLocalPlayer)
{
AdjustIconXToBlackBorder();
}
}
private void AdjustIconXToBlackBorder()
{
_icon.Value = new ClickableTextureComponent("",
new Rectangle(Tools.GetWidthInPlayArea() - 134,
290,
10 * Game1.pixelZoom,
10 * Game1.pixelZoom),
"",
"",
Game1.mouseCursors,
new Rectangle(50, 428, 10, 14),
Game1.pixelZoom,
false);
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShopHarvestPrices.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShopHarvestPrices : IDisposable
{
private readonly IModHelper _helper;
public ShopHarvestPrices(IModHelper helper)
{
_helper = helper;
}
public void ToggleOption(bool shopHarvestPrices)
{
_helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu;
if (shopHarvestPrices)
{
_helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu;
}
}
public void Dispose()
{
ToggleOption(false);
}
/// <summary>When a menu is open (<see cref="Game1.activeClickableMenu"/> isn't null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnRenderedActiveMenu(object sender, RenderedActiveMenuEventArgs e)
{
if (!(Game1.activeClickableMenu is ShopMenu menu)) return;
if (!(menu.hoveredItem is Item hoverItem)) return;
// draw shop harvest prices
int value = Tools.GetHarvestPrice(hoverItem);
if (value > 0)
{
int xPosition = menu.xPositionOnScreen - 30;
int yPosition = menu.yPositionOnScreen + 580;
IClickableMenu.drawTextureBox(
Game1.spriteBatch,
xPosition + 20,
yPosition - 52,
264,
108,
Color.White);
// Title "Harvest Price"
string textToRender = _helper.SafeGetString(LanguageKeys.HarvestPrice);
Game1.spriteBatch.DrawString(
Game1.dialogueFont,
textToRender,
new Vector2(xPosition + 30, yPosition - 38),
Color.Black * 0.2f);
Game1.spriteBatch.DrawString(
Game1.dialogueFont,
textToRender,
new Vector2(xPosition + 32, yPosition - 40),
Color.Black * 0.8f);
// Tree Icon
xPosition += 80;
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(xPosition, yPosition),
new Rectangle(60, 428, 10, 10),
Color.White,
0,
Vector2.Zero,
Game1.pixelZoom,
SpriteEffects.None,
0.85f);
// Coin
Game1.spriteBatch.Draw(
Game1.debrisSpriteSheet,
new Vector2(xPosition + 32, yPosition + 10),
Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 8, 16, 16),
Color.White,
0,
new Vector2(8, 8),
4,
SpriteEffects.None,
0.95f);
// Price
Game1.spriteBatch.DrawString(
Game1.dialogueFont,
value.ToString(),
new Vector2(xPosition + 50, yPosition + 6),
Color.Black * 0.2f);
Game1.spriteBatch.DrawString(
Game1.dialogueFont,
value.ToString(),
new Vector2(xPosition + 52, yPosition + 4),
Color.Black * 0.8f);
/*
* I have no Idea why this is here...
* As far as I can see it only overrides the existing Tooltip with a price that is 500 coins higher?
*
string hoverText = _helper.Reflection.GetField<string>(menu, "hoverText").GetValue();
string hoverTitle = _helper.Reflection.GetField<string>(menu, "boldTitleText").GetValue();
IReflectedMethod getHoveredItemExtraItemIndex = _helper.Reflection.GetMethod(menu, "getHoveredItemExtraItemIndex");
IReflectedMethod getHoveredItemExtraItemAmount = _helper.Reflection.GetMethod(menu, "getHoveredItemExtraItemAmount");
IClickableMenu.drawToolTip(
Game1.spriteBatch,
hoverText,
hoverTitle,
hoverItem,
menu.heldItem != null,
-1,
menu.currency,
getHoveredItemExtraItemIndex.Invoke<int>(new object[0]),
getHoveredItemExtraItemAmount.Invoke<int>(new object[0]),
null,
menu.hoverPrice);
*/
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowAccurateHearts.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Linq;
using System.Reflection;
namespace UIInfoSuite2.UIElements
{
internal class ShowAccurateHearts : IDisposable
{
#region Properties
private string[] _friendNames;
private SocialPage _socialPage;
private IModEvents _events;
private readonly int[][] _numArray = new int[][]
{
new int[] { 1, 1, 0, 1, 1 },
new int[] { 1, 1, 1, 1, 1 },
new int[] { 0, 1, 1, 1, 0 },
new int[] { 0, 0, 1, 0, 0 }
};
#endregion
#region Lifecycle
public ShowAccurateHearts(IModEvents events)
{
_events = events;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showAccurateHearts)
{
_events.Display.MenuChanged -= OnMenuChanged;
_events.Display.RenderedActiveMenu -= OnRenderedActiveMenu;
if (showAccurateHearts)
{
_events.Display.MenuChanged += OnMenuChanged;
_events.Display.RenderedActiveMenu += OnRenderedActiveMenu;
}
}
#endregion
#region Event subscriptions
private void OnRenderedActiveMenu(object sender, RenderedActiveMenuEventArgs e)
{
if (_socialPage == null)
{
ExtendMenuIfNeeded();
return;
}
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 2)
{
DrawHeartFills();
string hoverText = gameMenu.hoverText;
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText,
Game1.smallFont);
}
}
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
ExtendMenuIfNeeded();
}
#endregion
#region Logic
private void ExtendMenuIfNeeded()
{
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
foreach (var menu in gameMenu.pages)
{
if (menu is SocialPage page)
{
_socialPage = page;
_friendNames = _socialPage.names
.Select(name => name.ToString())
.ToArray();
break;
}
}
}
}
private void DrawHeartFills()
{
int slotPosition = (int)typeof(SocialPage)
.GetField(
"slotPosition",
BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(_socialPage);
int yOffset = 0;
for (int i = slotPosition; i < slotPosition + 5 && i < _friendNames.Length; ++i)
{
if (Game1.player.friendshipData.TryGetValue(_friendNames[i], out Friendship friendshipValues)
&& friendshipValues.Points > 0
&& friendshipValues.Points < Utility.GetMaximumHeartsForCharacter(Game1.getCharacterFromName(_friendNames[i])) * 250)
{
int pointsToNextHeart = friendshipValues.Points % 250;
int numHearts = friendshipValues.Points / 250;
int yPosition = Game1.activeClickableMenu.yPositionOnScreen + 130 + yOffset;
DrawEachIndividualSquare(numHearts, pointsToNextHeart, yPosition);
}
yOffset += 112;
}
}
private void DrawEachIndividualSquare(int friendshipLevel, int friendshipPoints, int yPosition)
{
int numberOfPointsToDraw = (int)((friendshipPoints) / 12.5);
int num2;
if (friendshipLevel >= 10)
{
num2 = 32 * (friendshipLevel - 10);
yPosition += 28;
}
else
{
num2 = 32 * friendshipLevel;
}
for (int i = 3; i >= 0 && numberOfPointsToDraw > 0; --i)
{
for (int j = 0; j < 5 && numberOfPointsToDraw > 0; ++j, --numberOfPointsToDraw)
{
if (_numArray[i][j] == 1)
{
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(
Game1.activeClickableMenu.xPositionOnScreen + 320 + num2 + j * 4,
yPosition + 14 + i * 4,
4,
4),
Color.Crimson);
}
}
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowBirthdayIcon.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowBirthdayIcon : IDisposable
{
#region Properties
private readonly PerScreen<List<NPC>> _birthdayNPCs = new(() => new());
private readonly PerScreen<List<ClickableTextureComponent>> _birthdayIcons = new(() => new());
private bool Enabled { get; set; }
private bool HideBirthdayIfFullFriendShip { get; set; }
private readonly IModHelper _helper;
#endregion
#region Life cycle
public ShowBirthdayIcon(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showBirthdayIcon)
{
Enabled = showBirthdayIcon;
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showBirthdayIcon)
{
CheckForBirthday();
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
public void ToggleDisableOnMaxFriendshipOption(bool hideBirthdayIfFullFriendShip)
{
HideBirthdayIfFullFriendShip = hideBirthdayIfFullFriendShip;
ToggleOption(Enabled);
}
#endregion
#region Event subscriptions
private void OnUpdateTicked(object? sender, UpdateTickedEventArgs e)
{
if (e.IsOneSecond)
{
CheckForGiftGiven();
}
}
private void OnDayStarted(object? sender, DayStartedEventArgs e)
{
CheckForBirthday();
}
private void OnRenderingHud(object? sender, RenderingHudEventArgs e)
{
if (!Game1.eventUp)
{
DrawBirthdayIcon();
}
}
private void OnRenderedHud(object? sender, RenderedHudEventArgs e)
{
if (!Game1.eventUp)
{
DrawHoverText();
}
}
#endregion
#region Logic
private void CheckForGiftGiven()
{
List<NPC> npcs = _birthdayNPCs.Value;
// Iterate from the end so that removing items doesn't affect indices
for (int i = npcs.Count - 1; i >= 0; i--)
{
Friendship? friendship = GetFriendshipWithNPC(npcs[i].Name);
if (friendship != null && friendship.GiftsToday > 0)
{
npcs.RemoveAt(i);
}
}
}
private void CheckForBirthday()
{
_birthdayNPCs.Value.Clear();
foreach (var location in Game1.locations)
{
foreach (var character in location.characters)
{
if (character.isBirthday(Game1.currentSeason, Game1.dayOfMonth))
{
Friendship? friendship = GetFriendshipWithNPC(character.Name);
if (friendship != null)
{
if (HideBirthdayIfFullFriendShip && friendship.Points >= Utility.GetMaximumHeartsForCharacter(character) * NPC.friendshipPointsPerHeartLevel)
continue;
_birthdayNPCs.Value.Add(character);
}
}
}
}
}
private static Friendship? GetFriendshipWithNPC(string name)
{
try
{
if (Game1.player.friendshipData.TryGetValue(name, out Friendship friendship))
return friendship;
else
return null;
}
catch (Exception ex)
{
ModEntry.MonitorObject.LogOnce("Error while getting information about the birthday of " + name, LogLevel.Error);
ModEntry.MonitorObject.Log(ex.ToString());
}
return null;
}
private void DrawBirthdayIcon()
{
_birthdayIcons.Value.Clear();
foreach (var npc in _birthdayNPCs.Value)
{
Rectangle headShot = npc.GetHeadShot();
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
float scale = 2.9f;
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(iconPosition.X, iconPosition.Y),
new Rectangle(228, 409, 16, 16),
Color.White,
0.0f,
Vector2.Zero,
scale,
SpriteEffects.None,
1f);
var birthdayIcon = new ClickableTextureComponent(
npc.Name,
new Rectangle(
iconPosition.X - 7,
iconPosition.Y - 2,
(int)(16.0 * scale),
(int)(16.0 * scale)),
null,
npc.Name,
npc.Sprite.Texture,
headShot,
2f);
birthdayIcon.draw(Game1.spriteBatch);
_birthdayIcons.Value.Add(birthdayIcon);
}
}
private void DrawHoverText()
{
var icons = _birthdayIcons.Value;
var npcs = _birthdayNPCs.Value;
if (icons.Count != npcs.Count)
{
ModEntry.MonitorObject.LogOnce($"{this.GetType().Name}: The number of tracked npcs and icons do not match", LogLevel.Error);
return;
}
for (int i = 0; i < icons.Count; i++)
{
if (icons[i].containsPoint(Game1.getMouseX(), Game1.getMouseY()))
{
string hoverText = string.Format(_helper.SafeGetString(LanguageKeys.NpcBirthday), npcs[i].displayName);
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText,
Game1.dialogueFont);
}
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowCalendarAndBillboardOnGameMenuButton.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using System.IO;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowCalendarAndBillboardOnGameMenuButton : IDisposable
{
#region Properties
private readonly PerScreen<ClickableTextureComponent> _showBillboardButton = new(createNewState: () =>
new ClickableTextureComponent(
new Rectangle(0, 0, 99, 60),
Game1.content.Load<Texture2D>(Path.Combine("Maps", "summer_town")),
new Rectangle(122, 291, 35, 20),
3f));
private readonly IModHelper _helper;
private readonly PerScreen<Item> _hoverItem = new();
private readonly PerScreen<Item> _heldItem = new();
#endregion
#region Lifecycle
public ShowCalendarAndBillboardOnGameMenuButton(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showCalendarAndBillboard)
{
ModEntry.RegisterCalendarAndQuestKeyBindings(_helper, showCalendarAndBillboard);
_helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu;
_helper.Events.Input.ButtonPressed -= OnButtonPressed;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showCalendarAndBillboard)
{
_helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu;
_helper.Events.Input.ButtonPressed += OnButtonPressed;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
#endregion
#region Event subscriptions
private void OnUpdateTicked(object sender, EventArgs e)
{
// Get hovered and hold item
_hoverItem.Value = Tools.GetHoveredItem();
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
List<IClickableMenu> menuList = gameMenu.pages;
if (menuList[0] is InventoryPage inventory)
{
_heldItem.Value = Game1.player.CursorSlotItem;
}
}
}
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
{
if (e.Button == SButton.MouseLeft)
ActivateBillboard();
else if (e.Button == SButton.ControllerA)
ActivateBillboard();
}
private void OnRenderedActiveMenu(object sender, EventArgs e)
{
if (_hoverItem.Value == null
&& Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 0
&& _heldItem.Value == null)
{
DrawBillboard();
}
}
#endregion
#region Logic
private void DrawBillboard()
{
var billboardButton = _showBillboardButton.Value;
billboardButton.bounds.X = Game1.activeClickableMenu.xPositionOnScreen + Game1.activeClickableMenu.width - 160;
billboardButton.bounds.Y = Game1.activeClickableMenu.yPositionOnScreen + Game1.activeClickableMenu.height -
// For compatiblity with BiggerBackpack mod
(_helper.ModRegistry.IsLoaded("spacechase0.BiggerBackpack") ? 230 : 300);
_showBillboardButton.Value = billboardButton;
_showBillboardButton.Value.draw(Game1.spriteBatch);
// Draw the mouse again to display it over the billboard
Game1.activeClickableMenu.drawMouse(Game1.spriteBatch);
if (_showBillboardButton.Value.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
{
string hoverText = Game1.getMouseX() <
_showBillboardButton.Value.bounds.X + _showBillboardButton.Value.bounds.Width / 2 ?
LanguageKeys.Calendar : LanguageKeys.Billboard;
IClickableMenu.drawHoverText(
Game1.spriteBatch,
_helper.SafeGetString(hoverText),
Game1.dialogueFont);
}
}
private void ActivateBillboard()
{
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 0
&& _heldItem.Value == null
&& _showBillboardButton.Value.containsPoint((int)Utility.ModifyCoordinateForUIScale(Game1.getMouseX()), (int)Utility.ModifyCoordinateForUIScale(Game1.getMouseY())))
{
if (Game1.questOfTheDay != null &&
string.IsNullOrEmpty(Game1.questOfTheDay.currentObjective))
Game1.questOfTheDay.currentObjective = "wat?";
Game1.activeClickableMenu =
new Billboard(!(Utility.ModifyCoordinateForUIScale(Game1.getMouseX()) <
_showBillboardButton.Value.bounds.X + _showBillboardButton.Value.bounds.Width / 2));
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowCropAndBarrelTime.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Locations;
using StardewValley.Menus;
using StardewValley.Objects;
using StardewValley.TerrainFeatures;
using System;
using System.Collections.Generic;
using System.Text;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowCropAndBarrelTime : IDisposable
{
private readonly Dictionary<int, string> _indexOfCropNames = new();
private readonly PerScreen<StardewValley.Object> _currentTile = new();
private readonly PerScreen<TerrainFeature> _terrain = new();
private readonly PerScreen<Building> _currentTileBuilding = new();
private readonly IModHelper _helper;
private readonly Dictionary<string, string> _indexOfDgaCropNames = new();
public ShowCropAndBarrelTime(IModHelper helper)
{
_helper = helper;
}
public void ToggleOption(bool showCropAndBarrelTimes)
{
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showCropAndBarrelTimes)
{
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
/// <summary>Raised after the game state is updated (≈60 times per second).</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (!e.IsMultipleOf(4))
return;
_currentTileBuilding.Value = null;
_currentTile.Value = null;
_terrain.Value = null;
var gamepadTile = Game1.player.CurrentTool != null ? Utility.snapToInt(Game1.player.GetToolLocation() / Game1.tileSize) : Utility.snapToInt(Game1.player.GetGrabTile());
var mouseTile = Game1.currentCursorTile;
var tile = (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0) ? gamepadTile : mouseTile;
if (Game1.currentLocation is BuildableGameLocation buildableLocation)
_currentTileBuilding.Value = buildableLocation.getBuildingAt(tile);
if (Game1.currentLocation != null)
{
if (Game1.currentLocation.Objects != null && Game1.currentLocation.Objects.TryGetValue(tile, out var currentObject))
_currentTile.Value = currentObject;
if (Game1.currentLocation.terrainFeatures != null && Game1.currentLocation.terrainFeatures.TryGetValue(tile, out var terrain))
_terrain.Value = terrain;
// Make sure that _terrain is null before overwriting it because Tea Saplings are added to terrainFeatures and not IndoorPot.bush
if (_terrain.Value == null && _currentTile.Value is IndoorPot pot)
{
if (pot.hoeDirt.Value != null)
_terrain.Value = pot.hoeDirt.Value;
if (pot.bush.Value != null)
_terrain.Value = pot.bush.Value;
}
}
}
public void Dispose()
{
ToggleOption(false);
}
/// <summary>Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open).</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
if (Game1.activeClickableMenu != null)
return;
var currentTileBuilding = _currentTileBuilding.Value;
var currentTile = _currentTile.Value;
var terrain = _terrain.Value;
int overrideX = -1;
int overrideY = -1;
// draw hover tooltip
if (currentTileBuilding != null && currentTileBuilding is Mill millBuilding && millBuilding.input.Value != null && !millBuilding.input.Value.isEmpty())
{
int wheatCount = 0;
int beetCount = 0;
foreach (Item item in millBuilding.input.Value.items)
{
if (item != null &&
!string.IsNullOrEmpty(item.Name))
{
switch (item.Name)
{
case "Wheat": wheatCount = item.Stack; break;
case "Beet": beetCount = item.Stack; break;
}
}
}
StringBuilder builder = new StringBuilder();
if (wheatCount > 0)
builder.Append(wheatCount + " wheat");
if (beetCount > 0)
{
if (wheatCount > 0)
builder.Append(Environment.NewLine);
builder.Append(beetCount + " beets");
}
if (builder.Length > 0)
{
if (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0)
{
var tilePosition = Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(new
Vector2(currentTileBuilding.tileX.Value, currentTileBuilding.tileY.Value) * Game1.tileSize));
overrideX = (int)(tilePosition.X + Utility.ModifyCoordinateForUIScale(32));
overrideY = (int)(tilePosition.Y + Utility.ModifyCoordinateForUIScale(32));
}
IClickableMenu.drawHoverText(
Game1.spriteBatch,
builder.ToString(),
Game1.smallFont, overrideX: overrideX, overrideY: overrideY);
}
}
else if (currentTile != null &&
(!currentTile.bigCraftable.Value ||
currentTile.MinutesUntilReady > 0))
{
if (currentTile.bigCraftable.Value &&
currentTile.MinutesUntilReady > 0 &&
currentTile.heldObject.Value != null &&
currentTile.Name != "Heater")
{
StringBuilder hoverText = new StringBuilder();
hoverText.AppendLine(currentTile.heldObject.Value.DisplayName);
if (currentTile is Cask)
{
Cask currentCask = currentTile as Cask;
hoverText.Append((int)(currentCask.daysToMature.Value / currentCask.agingRate.Value))
.Append(" " + _helper.SafeGetString(
LanguageKeys.DaysToMature));
}
else
{
int timeLeft = currentTile.MinutesUntilReady;
int longTime = timeLeft / 60;
string longText = LanguageKeys.Hours;
int shortTime = timeLeft % 60;
string shortText = LanguageKeys.Minutes;
// 1600 minutes per day if you go to bed at 2am, more if you sleep early.
if (timeLeft >= 1600)
{
// Unlike crops and casks, this is only an approximate number of days
// because of how time works while sleeping. It's close enough though.
longText = LanguageKeys.Days;
longTime = timeLeft / 1600;
shortText = LanguageKeys.Hours;
shortTime = (timeLeft % 1600);
// Hours below 1200 are 60 minutes per hour. Overnight it's 100 minutes per hour.
// We could just divide by 60 here but then you could see strange times like
// "2 days, 25 hours".
// This is a bit of a fudge since depending on the current time of day and when the
// farmer goes to bed, the night might happen earlier or last longer, but it's just
// an approximation; regardless the processing won't finish before tomorrow.
if (shortTime <= 1200)
shortTime /= 60;
else
shortTime = 20 + (shortTime - 1200) / 100;
}
if (longTime > 0)
hoverText.Append(longTime).Append(" ")
.Append(_helper.SafeGetString(longText))
.Append(", ");
hoverText.Append(shortTime).Append(" ")
.Append(_helper.SafeGetString(shortText));
}
if (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0)
{
var tilePosition = Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(new Vector2(currentTile.TileLocation.X, currentTile.TileLocation.Y) * Game1.tileSize));
overrideX = (int)(tilePosition.X + Utility.ModifyCoordinateForUIScale(32));
overrideY = (int)(tilePosition.Y + Utility.ModifyCoordinateForUIScale(32));
}
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText.ToString(),
Game1.smallFont, overrideX: overrideX, overrideY: overrideY);
}
}
else if (terrain != null)
{
if (terrain is HoeDirt)
{
HoeDirt hoeDirt = terrain as HoeDirt;
if (hoeDirt.crop != null &&
!hoeDirt.crop.dead.Value)
{
int num = 0;
if (hoeDirt.crop.fullyGrown.Value &&
hoeDirt.crop.dayOfCurrentPhase.Value > 0)
{
num = hoeDirt.crop.dayOfCurrentPhase.Value;
}
else
{
for (int i = 0; i < hoeDirt.crop.phaseDays.Count - 1; ++i)
{
if (hoeDirt.crop.currentPhase.Value == i)
num -= hoeDirt.crop.dayOfCurrentPhase.Value;
if (hoeDirt.crop.currentPhase.Value <= i)
num += hoeDirt.crop.phaseDays[i];
}
}
string? harvestName = this.GetCropHarvestName(hoeDirt.crop);
if (!String.IsNullOrEmpty(harvestName))
{
StringBuilder hoverText = new StringBuilder(harvestName).Append(": ");
if (num > 0)
{
hoverText.Append(num).Append(" ")
.Append(_helper.SafeGetString(
LanguageKeys.Days));
}
else
{
hoverText.Append(_helper.SafeGetString(
LanguageKeys.ReadyToHarvest));
}
if (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0)
{
var tilePosition = Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(new Vector2(terrain.currentTileLocation.X, terrain.currentTileLocation.Y) * Game1.tileSize));
overrideX = (int)(tilePosition.X + Utility.ModifyCoordinateForUIScale(32));
overrideY = (int)(tilePosition.Y + Utility.ModifyCoordinateForUIScale(32));
}
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText.ToString(),
Game1.smallFont, overrideX: overrideX, overrideY: overrideY);
}
}
}
else if (terrain is FruitTree)
{
FruitTree tree = terrain as FruitTree;
var text = new StardewValley.Object(new Debris(tree.indexOfFruit.Value, Vector2.Zero, Vector2.Zero).chunkType.Value, 1).DisplayName;
if (tree.daysUntilMature.Value > 0)
{
text += Environment.NewLine + tree.daysUntilMature.Value + " " +
_helper.SafeGetString(
LanguageKeys.DaysToMature);
}
if (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0)
{
var tilePosition = Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(new
Vector2(terrain.currentTileLocation.X, terrain.currentTileLocation.Y) * Game1.tileSize));
overrideX = (int)(tilePosition.X + Utility.ModifyCoordinateForUIScale(32));
overrideY = (int)(tilePosition.Y + Utility.ModifyCoordinateForUIScale(32));
}
IClickableMenu.drawHoverText(
Game1.spriteBatch,
text,
Game1.smallFont, overrideX: overrideX, overrideY: overrideY);
}
else if (terrain is Bush bush)
{
// Tea saplings (which are actually bushes)
if (bush.size.Value == Bush.greenTeaBush)
{
int teaAge = bush.getAge();
if (teaAge < 20)
{
string text = new StardewValley.Object(251, 1).DisplayName
+ $"\n{20 - teaAge} "
+ _helper.SafeGetString(LanguageKeys.DaysToMature);
if (Game1.options.gamepadControls && Game1.timerUntilMouseFade <= 0)
{
var tilePosition = Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(new Vector2(terrain.currentTileLocation.X, terrain.currentTileLocation.Y) * Game1.tileSize));
overrideX = (int)(tilePosition.X + Utility.ModifyCoordinateForUIScale(32));
overrideY = (int)(tilePosition.Y + Utility.ModifyCoordinateForUIScale(32));
}
IClickableMenu.drawHoverText(
Game1.spriteBatch,
text,
Game1.smallFont, overrideX: overrideX, overrideY: overrideY);
}
}
}
}
}
string? GetCropHarvestName(Crop crop)
{
if (crop.indexOfHarvest.Value > 0)
{
int itemId = crop.isWildSeedCrop() ? crop.whichForageCrop.Value : crop.indexOfHarvest.Value;
if (!_indexOfCropNames.TryGetValue(itemId, out string? harvestName)) {
harvestName = new StardewValley.Object(itemId, 1).DisplayName;
_indexOfCropNames.Add(itemId, harvestName);
}
return harvestName;
}
else if (ModEntry.DGA.IsCustomCrop(crop, out var dgaHelper))
{
string? cropId = null;
try
{
cropId = dgaHelper.GetFullId(crop)!;
if (!_indexOfDgaCropNames.TryGetValue(cropId, out string? harvestName)) {
var harvestCrop = dgaHelper.GetCropHarvest(crop);
if (harvestCrop == null)
return null;
harvestName = harvestCrop.DisplayName;
_indexOfDgaCropNames.Add(cropId, harvestName);
}
return harvestName;
}
catch (Exception e)
{
ModEntry.MonitorObject.LogOnce($"An error occured while retrieving the crop harvest name for {cropId ?? "unknownCrop"}.", LogLevel.Error);
ModEntry.MonitorObject.Log(e.ToString(), LogLevel.Debug);
return null;
}
}
else
{
return null;
}
}
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowItemEffectRanges.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Locations;
using System;
using System.Collections.Generic;
using System.Threading;
namespace UIInfoSuite2.UIElements
{
internal class ShowItemEffectRanges : IDisposable
{
#region Properties
private readonly PerScreen<List<Point>> _effectiveArea = new(createNewState: () => new List<Point>());
private readonly Mutex _mutex = new();
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowItemEffectRanges(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showItemEffectRanges)
{
_helper.Events.Display.Rendered -= OnRendered;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showItemEffectRanges)
{
_helper.Events.Display.Rendered += OnRendered;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
#endregion
#region Event subscriptions
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (!e.IsMultipleOf(4))
return;
if (_mutex.WaitOne())
{
try
{
_effectiveArea.Value.Clear();
}
finally
{
_mutex.ReleaseMutex();
}
}
if (Game1.activeClickableMenu == null && !Game1.eventUp)
{
UpdateEffectiveArea();
}
}
private void OnRendered(object sender, RenderedEventArgs e)
{
if (_mutex.WaitOne(0))
{
try
{
foreach (Point point in _effectiveArea.Value)
{
var position = new Vector2(point.X * Utility.ModifyCoordinateFromUIScale(Game1.tileSize), point.Y * Utility.ModifyCoordinateFromUIScale(Game1.tileSize));
Game1.spriteBatch.Draw(
Game1.mouseCursors,
Utility.ModifyCoordinatesForUIScale(Game1.GlobalToLocal(Utility.ModifyCoordinatesForUIScale(position))),
new Rectangle(194, 388, 16, 16),
Color.White * 0.7f,
0.0f,
Vector2.Zero,
Utility.ModifyCoordinateForUIScale(Game1.pixelZoom),
SpriteEffects.None,
0.01f);
}
}
finally
{
_mutex.ReleaseMutex();
}
}
}
#endregion
#region Logic
private void UpdateEffectiveArea()
{
int[][] arrayToUse;
List<StardewValley.Object> similarObjects;
// Junimo Hut is handled differently, because it is a building
if (Game1.currentLocation is BuildableGameLocation buildableLocation)
{
Building building = buildableLocation.getBuildingAt(Game1.GetPlacementGrabTile());
if (building is JunimoHut)
{
arrayToUse = GetDistanceArray(ObjectsWithDistance.JunimoHut);
foreach (var nextBuilding in buildableLocation.buildings)
{
if (nextBuilding is JunimoHut nextHut)
{
AddTilesToHighlightedArea(arrayToUse, nextHut.tileX.Value + 1, nextHut.tileY.Value + 1);
}
}
}
}
// Every other item is here
if (Game1.player.CurrentItem is StardewValley.Object currentItem && currentItem.isPlaceable())
{
string itemName
= currentItem.Name;
Vector2 currentTile = Game1.GetPlacementGrabTile();
Game1.isCheckingNonMousePlacement = !Game1.IsPerformingMousePlacement();
Vector2 validTile = Utility.snapToInt(Utility.GetNearbyValidPlacementPosition(Game1.player, Game1.currentLocation, currentItem, (int)currentTile.X * Game1.tileSize, (int)currentTile.Y * Game1.tileSize)) / Game1.tileSize;
Game1.isCheckingNonMousePlacement = false;
if (itemName.IndexOf("arecrow", StringComparison.OrdinalIgnoreCase) >= 0)
{
arrayToUse = itemName.Contains("eluxe") ?
GetDistanceArray(ObjectsWithDistance.DeluxeScarecrow, false, currentItem) :
GetDistanceArray(ObjectsWithDistance.Scarecrow, false, currentItem);
AddTilesToHighlightedArea(arrayToUse, (int)validTile.X, (int)validTile.Y);
similarObjects = GetSimilarObjectsInLocation("arecrow");
foreach (StardewValley.Object next in similarObjects)
{
arrayToUse = next.Name.IndexOf("eluxe", StringComparison.OrdinalIgnoreCase) >= 0 ?
GetDistanceArray(ObjectsWithDistance.DeluxeScarecrow, false, next) :
GetDistanceArray(ObjectsWithDistance.Scarecrow, false, next);
AddTilesToHighlightedArea(arrayToUse, (int)next.TileLocation.X, (int)next.TileLocation.Y);
}
}
else if (itemName.IndexOf("sprinkler", StringComparison.OrdinalIgnoreCase) >= 0)
{
// Relative tile positions to the placable items locations - need to pass coordinates
AddTilesToHighlightedArea(currentItem.GetSprinklerTiles(), (int)validTile.X, (int)validTile.Y);
similarObjects = GetSimilarObjectsInLocation("sprinkler");
foreach (StardewValley.Object next in similarObjects)
{
// Absolute tile positions
AddTilesToHighlightedArea(next.GetSprinklerTiles());
}
}
else if (itemName.IndexOf("bee house", StringComparison.OrdinalIgnoreCase) >= 0)
{
arrayToUse = GetDistanceArray(ObjectsWithDistance.Beehouse);
AddTilesToHighlightedArea(arrayToUse, (int)validTile.X, (int)validTile.Y);
}
}
}
private void AddTilesToHighlightedArea(IEnumerable<Vector2> tiles, int xPos = 0, int yPos = 0)
{
if (_mutex.WaitOne())
{
try
{
foreach (var tile in tiles)
{
var point = tile.ToPoint();
point.X += xPos;
point.Y += yPos;
_effectiveArea.Value.Add(point);
}
}
finally
{
_mutex.ReleaseMutex();
}
}
}
private void AddTilesToHighlightedArea(int[][] tileMap, int xPos = 0, int yPos = 0)
{
int xOffset = tileMap.Length / 2;
if (_mutex.WaitOne())
{
try
{
for (int i = 0; i < tileMap.Length; ++i)
{
int yOffset = tileMap[i].Length / 2;
for (int j = 0; j < tileMap[i].Length; ++j)
{
if (tileMap[i][j] == 1)
_effectiveArea.Value.Add(new Point(xPos + i - xOffset, yPos + j - yOffset));
}
}
}
finally
{
_mutex.ReleaseMutex();
}
}
}
private List<StardewValley.Object> GetSimilarObjectsInLocation(string nameContains)
{
List<StardewValley.Object> result = new List<StardewValley.Object>();
if (!string.IsNullOrEmpty(nameContains))
{
nameContains = nameContains.ToLower();
var objects = Game1.currentLocation.Objects;
foreach (var nextThing in objects.Values)
{
if (nextThing.name.ToLower().Contains(nameContains))
result.Add(nextThing);
}
}
return result;
}
#region Distance map
private enum ObjectsWithDistance
{
JunimoHut,
Beehouse,
Scarecrow,
DeluxeScarecrow,
Sprinkler,
QualitySprinkler,
IridiumSprinkler,
PrismaticSprinkler
}
private int[][] GetDistanceArray(ObjectsWithDistance type, bool hasPressureNozzle = false, StardewValley.Object? instance = null)
{
switch (type)
{
case ObjectsWithDistance.JunimoHut:
return GetCircularMask(100, maxDisplaySquareRadius: 8);
case ObjectsWithDistance.Beehouse:
return GetCircularMask(4.19, exceptionalDistance: 5, onlyClearExceptions: true);
case ObjectsWithDistance.Scarecrow:
return GetCircularMask((instance?.GetRadiusForScarecrow() ?? 9) - 0.01);
case ObjectsWithDistance.DeluxeScarecrow:
return GetCircularMask((instance?.GetRadiusForScarecrow() ?? 17) - 0.01);
case ObjectsWithDistance.Sprinkler:
return hasPressureNozzle ? GetCircularMask(100, maxDisplaySquareRadius: 1) : GetCircularMask(1);
case ObjectsWithDistance.QualitySprinkler:
return hasPressureNozzle ? GetCircularMask(100, maxDisplaySquareRadius: 2) : GetCircularMask(100, maxDisplaySquareRadius: 1);
case ObjectsWithDistance.IridiumSprinkler:
return hasPressureNozzle ? GetCircularMask(100, maxDisplaySquareRadius: 3) : GetCircularMask(100, maxDisplaySquareRadius: 2);
case ObjectsWithDistance.PrismaticSprinkler:
return GetCircularMask(3.69, exceptionalDistance: Math.Sqrt(18), onlyClearExceptions: false);
default:
return null;
}
}
private static int[][] GetCircularMask(double maxDistance, double? exceptionalDistance = null, bool? onlyClearExceptions = null, int? maxDisplaySquareRadius = null)
{
int radius = Math.Max((int)Math.Ceiling(maxDistance), exceptionalDistance.HasValue ? (int)Math.Ceiling(exceptionalDistance.Value) : 0);
radius = Math.Min(radius, maxDisplaySquareRadius.HasValue ? maxDisplaySquareRadius.Value : radius);
int size = 2 * radius + 1;
int[][] result = new int[size][];
for (int i = 0; i < size; i++)
{
result[i] = new int[size];
for (int j = 0; j < size; j++)
{
double distance = GetDistance(i, j, radius);
int val = (IsInDistance(maxDistance, distance)
|| (IsDistanceDirectionOK(i, j, radius, onlyClearExceptions) && IsExceptionalDistanceOK(exceptionalDistance, distance)))
? 1 : 0;
result[i][j] = val;
}
}
return result;
}
private static bool IsDistanceDirectionOK(int i, int j, int radius, bool? onlyClearExceptions)
{
return onlyClearExceptions.HasValue && onlyClearExceptions.Value ? (radius - j) == 0 || (radius - i) == 0 : true;
}
private static bool IsExceptionalDistanceOK(double? exceptionalDistance, double distance)
{
return exceptionalDistance.HasValue && exceptionalDistance.Value == distance;
}
private static bool IsInDistance(double maxDistance, double distance)
{
return distance <= maxDistance;
}
private static double GetDistance(int i, int j, int radius)
{
return Math.Sqrt((radius - i) * (radius - i) + (radius - j) * (radius - j));
}
#endregion
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowItemHoverInformation.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Locations;
using StardewValley.Menus;
using StardewValley.Objects;
using StardewValley.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowItemHoverInformation : IDisposable
{
private readonly Dictionary<string, List<KeyValuePair<int, int>>> _prunedRequiredBundles = new();
private readonly ClickableTextureComponent _bundleIcon =
new(
new Rectangle(0, 0, Game1.tileSize, Game1.tileSize),
Game1.mouseCursors,
new Rectangle(331, 374, 15, 14),
3f);
private readonly ClickableTextureComponent _museumIcon;
private readonly ClickableTextureComponent _shippingBottomIcon =
new(
new Rectangle(0, 0, Game1.tileSize, Game1.tileSize),
Game1.mouseCursors,
new Rectangle(526, 218, 30, 22),
1.2f);
private readonly ClickableTextureComponent _shippingTopIcon =
new(
new Rectangle(0, 0, Game1.tileSize, Game1.tileSize),
Game1.mouseCursors,
new Rectangle(134, 236, 30, 15),
1.2f);
private readonly PerScreen<Item?> _hoverItem = new();
private CommunityCenter _communityCenter;
private LibraryMuseum _libraryMuseum;
private Dictionary<string, string> _bundleNameOverrides;
private readonly IModHelper _helper;
public ShowItemHoverInformation(IModHelper helper)
{
_helper = helper;
var gunther = Game1.getCharacterFromName("Gunther");
if (gunther == null) {
ModEntry.MonitorObject.Log($"{this.GetType().Name}: Could not find Gunther in the game, creating a fake one for ourselves.", LogLevel.Warn);
gunther = new NPC() {
Name = "Gunther",
Age = 0,
Sprite = new AnimatedSprite("Characters\\Gunther"),
};
}
_museumIcon = new(
new Rectangle(0, 0, Game1.tileSize, Game1.tileSize),
gunther.Sprite.Texture,
gunther.GetHeadShot(),
Game1.pixelZoom);
}
public void ToggleOption(bool showItemHoverInformation)
{
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.Player.InventoryChanged -= OnInventoryChanged;
_helper.Events.Display.Rendered -= OnRendered;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.Display.Rendering -= OnRendering;
if (showItemHoverInformation)
{
_communityCenter = Game1.getLocationFromName("CommunityCenter") as CommunityCenter;
_libraryMuseum = Game1.getLocationFromName("ArchaeologyHouse") as LibraryMuseum;
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Player.InventoryChanged += OnInventoryChanged;
_helper.Events.Display.Rendered += OnRendered;
_helper.Events.Display.RenderedHud += OnRenderedHud;
_helper.Events.Display.Rendering += OnRendering;
}
}
public void Dispose()
{
ToggleOption(false);
}
// [EventPriority(EventPriority.Low)]
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
// NB The Custom Community Center mod is only ready at this point
if (_helper.GameContent.CurrentLocaleConstant == LocalizedContentManager.LanguageCode.en && _helper.ModRegistry.IsLoaded("blueberry.CustomCommunityCentre"))
_bundleNameOverrides = GetEnglishNamesForCustomCommunityCenterBundles();
else
_bundleNameOverrides = null;
PopulateRequiredBundles();
}
/// <summary>Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnRendering(object sender, EventArgs e)
{
_hoverItem.Value = Tools.GetHoveredItem();
}
/// <summary>Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnRenderedHud(object sender, EventArgs e)
{
if (Game1.activeClickableMenu == null)
{
DrawAdvancedTooltip();
}
}
/// <summary>Raised after the game draws to the sprite patch in a draw tick, just before the final sprite batch is rendered to the screen.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnRendered(object sender, EventArgs e)
{
if (Game1.activeClickableMenu != null)
{
DrawAdvancedTooltip();
}
}
/// <summary>Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the current player.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnInventoryChanged(object sender, InventoryChangedEventArgs e)
{
if (e.IsLocalPlayer)
this.PopulateRequiredBundles();
}
/// Retrieve English bundle names for Custom Community Center bundles as the bundle id is not the bundle's English name.
/// For example, the bundle id is "Custom_blueberry_Kitchen_Area0_Bundle0" but the name is "Baker's"
private Dictionary<string, string> GetEnglishNamesForCustomCommunityCenterBundles()
{
try
{
Dictionary<string, string> englishNames = new();
var bundleNamesAsset = _helper.GameContent.Load<Dictionary<string, string>>(@"Strings\BundleNames");
foreach (var namePair in bundleNamesAsset)
{
if (namePair.Key != namePair.Value)
englishNames[namePair.Key] = namePair.Value;
}
return englishNames;
}
catch (Exception e)
{
ModEntry.MonitorObject.LogOnce("Failed to retrieve English names for Custom Community Center bundles. Custom bundle names may be displayed incorrectly.", LogLevel.Warn);
ModEntry.MonitorObject.Log(e.ToString());
return null;
}
}
private void PopulateRequiredBundles()
{
_prunedRequiredBundles.Clear();
if (!Game1.player.mailReceived.Contains("JojaMember"))
{
foreach (var bundle in Game1.netWorldState.Value.BundleData)
{
string[] bundleRoomInfo = bundle.Key.Split('/');
string bundleRoom = bundleRoomInfo[0];
int areaNum = CommunityCenter.getAreaNumberFromName(bundleRoom);
if (_communityCenter.shouldNoteAppearInArea(areaNum))
{
int bundleNumber = bundleRoomInfo[1].SafeParseInt32();
string[] bundleInfo = bundle.Value.Split('/');
string bundleName = _helper.GameContent.CurrentLocaleConstant == LocalizedContentManager.LanguageCode.en || int.TryParse(bundleInfo[^1], out _)
? bundleInfo[0]
: bundleInfo[^1];
string[] bundleValues = bundleInfo[2].Split(' ');
List<KeyValuePair<int, int>> source = new List<KeyValuePair<int, int>>();
for (int i = 0; i < bundleValues.Length; i += 3)
{
int bundleValue = bundleValues[i].SafeParseInt32();
int quality = bundleValues[i + 2].SafeParseInt32();
if (bundleValue != -1 &&
!_communityCenter.bundles[bundleNumber][i / 3])
{
source.Add(new KeyValuePair<int, int>(bundleValue, quality));
}
}
if (source.Count > 0)
{
if (_bundleNameOverrides != null && _bundleNameOverrides.TryGetValue(bundleName, out string value))
bundleName = value;
_prunedRequiredBundles.Add(bundleName, source);
}
}
}
}
}
//private Item _lastHoverItem;
//private int lastStackSize;
//private int lastItemPrice;
//private int lastStackPrice;
//private int lastCropPrice;
//private int lastTruePrice;
//private int lastWindowWidth;
//private string lastRequiredBundleName;
private void DrawAdvancedTooltip()
{
if (_hoverItem.Value != null
&& !(_hoverItem.Value is StardewValley.Tools.MeleeWeapon weapon && weapon.isScythe())
&& !(_hoverItem.Value is StardewValley.Tools.FishingRod))
{
var hoveredObject = _hoverItem.Value as StardewValley.Object;
int itemPrice = Tools.GetSellToStorePrice(_hoverItem.Value);
int stackPrice = 0;
if (itemPrice > 0 && _hoverItem.Value.Stack > 1)
stackPrice = itemPrice * _hoverItem.Value.Stack;
int cropPrice = Tools.GetHarvestPrice(_hoverItem.Value);
bool notDonatedYet = _libraryMuseum.isItemSuitableForDonation(_hoverItem.Value);
bool notShippedYet = (hoveredObject != null
&& hoveredObject.countsForShippedCollection()
&& !Game1.player.basicShipped.ContainsKey(hoveredObject.ParentSheetIndex)
&& hoveredObject.Type != "Fish");
if (notShippedYet && hoveredObject != null && ModEntry.DGA.IsCustomObject(hoveredObject, out var dgaHelper))
{
// NB For DGA items, Game1.player.basicShipped.ContainsKey(hoveredObject.ParentSheetIndex) will always be false
// and Object.countsForShippedCollection bypasses the type and category checks
try
{
// For shipping, DGA patches:
// - CollectionsPage()
// - ShippingMenu.parseItems()
// - StardewValley.Object.countsForShippedCollection()
// - StardewValley.Object.isIndexOkForBasicShippedCategory()
// But it doesn't patch Utility.getFarmerItemsShippedPercent() which determines if the requirements for the achievement are met.
// This means that DGA items do not (yet) count for the "Full Shipment" achievement even though they appear in the collections page.
// Nonetheless, show the icon if that item is still hidden in the collections page.
int dgaId = dgaHelper.GetDgaObjectFakeId(hoveredObject);
string t = hoveredObject.Type;
bool inCollectionsPage = !(t.Contains("Arch") || t.Contains("Fish") || t.Contains("Mineral") || t.Contains("Cooking"))
&& StardewValley.Object.isPotentialBasicShippedCategory(dgaId, hoveredObject.Category.ToString());
notShippedYet = inCollectionsPage && !Game1.player.basicShipped.ContainsKey(dgaId);
}
catch (Exception e)
{
ModEntry.MonitorObject.LogOnce($"An error occured while checking if the DGA item {hoveredObject.Name} has been shipped.", LogLevel.Error);
ModEntry.MonitorObject.Log(e.ToString(), LogLevel.Debug);
}
}
string? requiredBundleName = null;
// Bundle items must be "small" objects. This avoids marking other kinds of objects as needed, such as Chest (id 130), Recycling Machine (id 20), etc...
if (hoveredObject != null && !hoveredObject.bigCraftable.Value && hoveredObject is not Furniture)
{
foreach (var requiredBundle in _prunedRequiredBundles)
{
if (requiredBundle.Value.Any(itemQuality => itemQuality.Key == hoveredObject.ParentSheetIndex && hoveredObject.Quality >= itemQuality.Value))
{
requiredBundleName = requiredBundle.Key;
break;
}
}
}
var drawPositionOffset = new Vector2();
int windowWidth, windowHeight;
int bundleHeaderWidth = 0;
if (!string.IsNullOrEmpty(requiredBundleName))
{
// bundleHeaderWidth = ((bundleIcon.Width * 3 = 45) - 7 = 38) + 3 + bundleTextSize.X + 3 + ((shippingBin.Width * 1.2 = 36) - 12 = 24)
bundleHeaderWidth = 68 + (int)Game1.dialogueFont.MeasureString(requiredBundleName).X;
}
int itemTextWidth = (int)Game1.smallFont.MeasureString(itemPrice.ToString()).X;
int stackTextWidth = (int)Game1.smallFont.MeasureString(stackPrice.ToString()).X;
int cropTextWidth = (int)Game1.smallFont.MeasureString(cropPrice.ToString()).X;
int minTextWidth = (int)Game1.smallFont.MeasureString("000").X;
// largestTextWidth = 12 + 4 + (icon.Width = 32) + 4 + max(textSize.X) + 8 + 16
int largestTextWidth = 76 + Math.Max(minTextWidth, Math.Max(stackTextWidth, Math.Max(itemTextWidth, cropTextWidth)));
windowWidth = Math.Max(bundleHeaderWidth, largestTextWidth);
windowHeight = 20 + 16;
if (itemPrice > 0)
windowHeight += 40;
if (stackPrice > 0)
windowHeight += 40;
if (cropPrice > 0)
windowHeight += 40;
if (!string.IsNullOrEmpty(requiredBundleName))
{
windowHeight += 4;
drawPositionOffset.Y += 4;
}
// Minimal window dimensions
windowHeight = Math.Max(windowHeight,
40);
windowWidth = Math.Max(windowWidth, 40);
int windowY = Game1.getMouseY() + 20;
int windowX = Game1.getMouseX() - 25 - windowWidth;
// Adjust the tooltip's position when it overflows
var safeArea = Utility.getSafeArea();
if (windowY + windowHeight > safeArea.Bottom)
windowY = safeArea.Bottom - windowHeight;
if (Game1.getMouseX() + 300 > safeArea.Right)
windowX = safeArea.Right - 350 - windowWidth;
else if (windowX < safeArea.Left)
windowX = Game1.getMouseX() + 350;
Vector2 windowPos = new Vector2(windowX, windowY);
Vector2 drawPosition = windowPos + new Vector2(16, 20) + drawPositionOffset;
// Icons are drawn in 32x40 cells. The small font has a cap height of 18 and an offset of (2, 6)
int rowHeight = 40;
Vector2 iconCenterOffset = new Vector2(16, 20);
Vector2 textOffset = new Vector2(32 + 4, (rowHeight - 18) / 2 - 6);
if (itemPrice > 0 || stackPrice > 0 || cropPrice > 0 || !String.IsNullOrEmpty(requiredBundleName) || notDonatedYet || notShippedYet)
{
IClickableMenu.drawTextureBox(
Game1.spriteBatch,
Game1.menuTexture,
new Rectangle(0, 256, 60, 60),
(int)windowPos.X,
(int)windowPos.Y,
windowWidth,
windowHeight,
Color.White);
}
if (itemPrice > 0)
{
Game1.spriteBatch.Draw(
Game1.debrisSpriteSheet,
drawPosition + iconCenterOffset,
Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 8, 16, 16),
Color.White,
0,
new Vector2(8, 8),
Game1.pixelZoom,
SpriteEffects.None,
0.95f);
this.DrawSmallTextWithShadow(Game1.spriteBatch, itemPrice.ToString(), drawPosition + textOffset);
drawPosition.Y += rowHeight;
}
if (stackPrice > 0)
{
Vector2 overlapOffset = new Vector2(0, 10);
Game1.spriteBatch.Draw(
Game1.debrisSpriteSheet,
drawPosition + iconCenterOffset - overlapOffset / 2,
Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 8, 16, 16),
Color.White,
0,
new Vector2(8, 8),
Game1.pixelZoom,
SpriteEffects.None,
0.95f);
Game1.spriteBatch.Draw(
Game1.debrisSpriteSheet,
drawPosition + iconCenterOffset + overlapOffset / 2,
Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 8, 16, 16),
Color.White,
0,
new Vector2(8, 8),
Game1.pixelZoom,
SpriteEffects.None,
0.95f);
this.DrawSmallTextWithShadow(Game1.spriteBatch, stackPrice.ToString(), drawPosition + textOffset);
drawPosition.Y += rowHeight;
}
if (cropPrice > 0)
{
Game1.spriteBatch.Draw(
Game1.mouseCursors,
drawPosition + iconCenterOffset,
new Rectangle(60, 428, 10, 10),
Color.White,
0.0f,
new Vector2(5, 5),
Game1.pixelZoom * 0.75f,
SpriteEffects.None,
0.85f);
this.DrawSmallTextWithShadow(Game1.spriteBatch, cropPrice.ToString(), drawPosition + textOffset);
}
if (notDonatedYet)
{
Game1.spriteBatch.Draw(
_museumIcon.texture,
windowPos + new Vector2(2, windowHeight + 8),
_museumIcon.sourceRect,
Color.White,
0f,
new Vector2(_museumIcon.sourceRect.Width / 2, _museumIcon.sourceRect.Height),
2,
SpriteEffects.None,
0.86f);
}
if (!string.IsNullOrEmpty(requiredBundleName))
{
// Draws a 30x42 bundle icon offset by (-7, -13) from the top-left corner of the window
// and the 36px high banner with the bundle name
this.DrawBundleBanner(requiredBundleName, windowPos + new Vector2(-7, -13), windowWidth);
}
if (notShippedYet)
{
// Draws a 36x28 shipping bin offset by (-24, -6) from the top-right corner of the window
var shippingBinDims = new Vector2(30, 24);
this.DrawShippingBin(Game1.spriteBatch, windowPos + new Vector2(windowWidth - 6, 8), shippingBinDims / 2);
}
//memorize the result to save processing time when calling again with same values
//_lastHoverItem = (_lastHoverItem != _hoverItem.Value) ? _hoverItem.Value : _lastHoverItem;
//lastItemPrice = (itemPrice != lastItemPrice) ? itemPrice : lastItemPrice;
//lastCropPrice = (lastCropPrice != cropPrice) ? cropPrice : lastCropPrice;
//lastStackPrice = (lastStackPrice != stackPrice) ? stackPrice : lastStackPrice;
//lastTruePrice = (lastTruePrice != truePrice) ? truePrice : lastTruePrice;
//lastWindowWidth = (lastWindowWidth != windowWidth) ? windowWidth : lastWindowWidth;
//lastRequiredBundleName = (lastRequiredBundleName != requiredBundleName) ? requiredBundleName : lastRequiredBundleName;
//lastStackSize = (_hoverItem.Value != null && lastStackSize != _hoverItem.Value.Stack) ? _hoverItem.Value.Stack : lastStackSize;
}
}
private void DrawSmallTextWithShadow(SpriteBatch b, string text, Vector2 position)
{
b.DrawString(Game1.smallFont, text, position + new Vector2(2, 2), Game1.textShadowColor);
b.DrawString(Game1.smallFont, text, position, Game1.textColor);
}
private void DrawBundleBanner(string bundleName, Vector2 position, int windowWidth)
{
// NB The dialogue font has a cap height of 30 and an offset of (3, 6)
int bundleBannerX = (int)position.X;
int bundleBannerY = (int)position.Y + 3;
int cellCount = 36;
int solidCells = 6;
int cellWidth = windowWidth / cellCount;
for (int cell = 0; cell < cellCount; ++cell)
{
float fadeAmount = 0.92f - (cell < solidCells ? 0 : 1.0f * (cell-solidCells)/(cellCount-solidCells));
Game1.spriteBatch.Draw(
Game1.staminaRect,
new Rectangle(bundleBannerX + cell * cellWidth, bundleBannerY, cellWidth, 36),
Color.Crimson * fadeAmount);
}
Game1.spriteBatch.Draw(
Game1.mouseCursors,
position,
_bundleIcon.sourceRect,
Color.White,
0f,
Vector2.Zero,
_bundleIcon.scale,
SpriteEffects.None,
0.86f);
Game1.spriteBatch.DrawString(
Game1.dialogueFont,
bundleName,
position + new Vector2(_bundleIcon.sourceRect.Width * _bundleIcon.scale + 3, 0),
Color.White);
}
private void DrawShippingBin(SpriteBatch b, Vector2 position, Vector2 origin)
{
var shippingBinOffset = new Vector2(0, 2);
// var shippingBinLidOffset = Vector2.Zero;
// NB This is not the texture used to draw the shipping bin on the farm map.
// The one for the farm is located in "Buildings\Shipping Bin".
Game1.spriteBatch.Draw(
_shippingBottomIcon.texture,
position,
_shippingBottomIcon.sourceRect,
Color.White,
0f,
origin - shippingBinOffset,
_shippingBottomIcon.scale,
SpriteEffects.None,
0.86f);
Game1.spriteBatch.Draw(
_shippingTopIcon.texture,
position,
_shippingTopIcon.sourceRect,
Color.White,
0f,
origin,
_shippingTopIcon.scale,
SpriteEffects.None,
0.86f);
}
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowQueenOfSauceIcon.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Objects;
using System;
using System.Collections.Generic;
using System.Reflection;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowQueenOfSauceIcon : IDisposable
{
#region Properties
private Dictionary<string, string> _recipesByDescription = new();
private Dictionary<string, string> _recipes = new();
private CraftingRecipe _todaysRecipe;
private readonly PerScreen<bool> _drawQueenOfSauceIcon = new();
//private bool _drawDishOfDayIcon = false;
private readonly PerScreen<ClickableTextureComponent> _icon = new();
private readonly IModHelper _helper;
#endregion
#region Life cycle
public ShowQueenOfSauceIcon(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showQueenOfSauceIcon)
{
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
_helper.Events.GameLoop.SaveLoaded -= OnSaveLoaded;
if (showQueenOfSauceIcon)
{
LoadRecipes();
CheckForNewRecipe();
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
_helper.Events.GameLoop.SaveLoaded += OnSaveLoaded;
}
}
#endregion
#region Event subscriptions
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
CheckForNewRecipe();
}
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
CheckForNewRecipe();
}
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (e.IsOneSecond && _drawQueenOfSauceIcon.Value && Game1.player.knowsRecipe(_todaysRecipe.name))
_drawQueenOfSauceIcon.Value = false;
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
if (!Game1.eventUp)
{
if (_drawQueenOfSauceIcon.Value)
{
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
_icon.Value = new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
Game1.mouseCursors,
new Rectangle(609, 361, 28, 28),
1.3f);
_icon.Value.draw(Game1.spriteBatch);
}
//if (_drawDishOfDayIcon)
//{
// Point iconLocation = IconHandler.Handler.GetNewIconPosition();
// float scale = 2.9f;
// Game1.spriteBatch.Draw(
// Game1.objectSpriteSheet,
// new Vector2(iconLocation.X, iconLocation.Y),
// new Rectangle(306, 291, 14, 14),
// Color.White,
// 0,
// Vector2.Zero,
// scale,
// SpriteEffects.None,
// 1f);
// ClickableTextureComponent texture =
// new ClickableTextureComponent(
// _gus.Name,
// new Rectangle(
// iconLocation.X - 7,
// iconLocation.Y - 2,
// (int)(16.0 * scale),
// (int)(16.0 * scale)),
// null,
// _gus.Name,
// _gus.Sprite.Texture,
// _gus.GetHeadShot(),
// 2f);
// texture.draw(Game1.spriteBatch);
// if (texture.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
// {
// IClickableMenu.drawHoverText(
// Game1.spriteBatch,
// "Gus is selling " + Game1.dishOfTheDay.DisplayName + " recipe today!",
// Game1.dialogueFont);
// }
//}
}
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
if (_drawQueenOfSauceIcon.Value && !Game1.IsFakedBlackScreen() && (_icon.Value?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false))
{
IClickableMenu.drawHoverText(Game1.spriteBatch, _helper.SafeGetString(LanguageKeys.TodaysRecipe) + _todaysRecipe.DisplayName, Game1.dialogueFont);
}
}
#endregion
#region Logic
private void LoadRecipes()
{
if (_recipes.Count == 0)
{
_recipes = Game1.content.Load<Dictionary<string, string>>("Data\\TV\\CookingChannel");
foreach (var next in _recipes)
{
string[] values = next.Value.Split('/');
if (values.Length > 1)
{
_recipesByDescription[values[1]] = values[0];
}
}
}
}
private void CheckForNewRecipe()
{
int recipiesKnownBeforeTvCall = Game1.player.cookingRecipes.Count();
string[] dialogue = typeof(TV).GetMethod("getWeeklyRecipe", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(new TV(), null) as string[];
_todaysRecipe = new CraftingRecipe(_recipesByDescription.SafeGet(dialogue[0]), true);
if (Game1.player.cookingRecipes.Count() > recipiesKnownBeforeTvCall)
Game1.player.cookingRecipes.Remove(_todaysRecipe.name);
_drawQueenOfSauceIcon.Value = (Game1.dayOfMonth
% 7 == 0 || (Game1.dayOfMonth - 3) % 7 == 0)
&& Game1.stats.DaysPlayed > 5 && !Game1.player.knowsRecipe(_todaysRecipe.name);
}
//private void FindGus()
//{
// foreach (var location in Game1.locations)
// {
// foreach (var npc in location.characters)
// {
// if (npc.Name == "Gus")
// {
// _gus = npc;
// break;
// }
// }
// if (_gus != null)
// break;
// }
//}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowRainyDayIcon.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowRainyDayIcon : IDisposable
{
#region Properties
private class LocationWeather
{
internal bool IsRainyTomorrow { get; set; }
internal Rectangle? SpriteLocation { get; set; }
internal string HoverText { get; set; }
internal ClickableTextureComponent IconComponent { get; set; }
}
private readonly LocationWeather _valleyWeather = new();
private readonly LocationWeather _islandWeather = new();
private Texture2D _iconSheet;
private Color[] _weatherIconColors;
private const int WeatherSheetWidth = 81;
private const int WeatherSheetHeight = 18;
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowRainyDayIcon(IModHelper helper)
{
_helper = helper;
CreateTileSheet();
}
public void Dispose()
{
ToggleOption(false);
_iconSheet.Dispose();
}
public void ToggleOption(bool showRainyDay)
{
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
if (showRainyDay)
{
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
}
}
#endregion
#region Event subscriptions
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
GetWeatherIconSpriteLocation();
if (Game1.eventUp)
{
return;
}
RenderLocationWeatherIcon(_valleyWeather);
if (HasVisitedIsland())
{
RenderLocationWeatherIcon(_islandWeather);
}
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// Show text on hover
RenderWeatherHoverText(_valleyWeather);
if (HasVisitedIsland())
{
RenderWeatherHoverText(_islandWeather);
}
}
private void RenderLocationWeatherIcon(LocationWeather weather)
{
// Escape if we don't have that weather or a sprite
if (!weather.IsRainyTomorrow || !weather.SpriteLocation.HasValue) return;
// Draw icon
var iconPosition = IconHandler.Handler.GetNewIconPosition();
weather.IconComponent =
new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
_iconSheet,
weather.SpriteLocation.Value,
8 / 3f);
weather.IconComponent.draw(Game1.spriteBatch);
}
private static void RenderWeatherHoverText(LocationWeather weather)
{
var hasMouse = weather.IconComponent?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false;
var hasText = !string.IsNullOrEmpty(weather.HoverText);
if (weather.IsRainyTomorrow && hasMouse && hasText)
{
IClickableMenu.drawHoverText(
Game1.spriteBatch,
weather.HoverText,
Game1.dialogueFont
);
}
}
#endregion
#region Logic
private static bool HasVisitedIsland()
{
return Game1.MasterPlayer.mailReceived.Contains("willyBoatFixed");
}
private static int GetWeatherForTomorrow()
{
var date = new WorldDate(Game1.Date);
++date.TotalDays;
var tomorrowWeather = Game1.IsMasterGame
? Game1.weatherForTomorrow
: Game1.netWorldState.Value.WeatherForTomorrow;
return Game1.getWeatherModificationsForDate(date, tomorrowWeather);
}
private static int GetIslandWeatherForTomorrow()
{
return Game1.netWorldState.Value.GetWeatherForLocation(GameLocation.LocationContext.Island).weatherForTomorrow.Value;
}
/// <summary>
/// Creates a custom tilesheet for weather icons.
/// Meant to mimic the TV screen, which has a border around it, while the individual icons in the Cursors tilesheet don't have a border
/// Extracts the border, and each individual weather icon and stitches them together into one separate sheet
///</summary>
private void CreateTileSheet()
{
ModEntry.MonitorObject.Log("Setting up icon sheet", LogLevel.Info);
// Setup Texture sheet as a copy, so as not to disturb existing sprites
_iconSheet = new Texture2D(Game1.graphics.GraphicsDevice, WeatherSheetWidth, WeatherSheetHeight);
_weatherIconColors = new Color[WeatherSheetWidth * WeatherSheetHeight];
var cursorColors = new Color[Game1.mouseCursors.Width * Game1.mouseCursors.Height];
var bounds = new Rectangle(0, 0, Game1.mouseCursors.Width, Game1.mouseCursors.Height);
Game1.mouseCursors.GetData(cursorColors);
var subTextureColors = new Color[15 * 15];
// Copy over the bits we want
// Border from TV screen
Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(499, 307, 15, 15));
// Copy to each destination
for (var i = 0; i < 3; i++)
{
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth,
new Rectangle(i * 15, 0, 15, 15));
}
// Add in expanded sprites for the island parrot
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(45, 0, 15, 15));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(63, 0, 15, 15));
subTextureColors = new Color[13 * 13];
// Rainy Weather
Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(504, 333, 13, 13));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(1, 1, 13, 13));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(46, 1, 13, 13));
// Stormy Weather
Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(426, 346, 13, 13));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(16, 1, 13, 13));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(64, 1, 13, 13));
// Snowy Weather
Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(465, 346, 13, 13));
Tools.SetSubTexture(subTextureColors,
_weatherIconColors, WeatherSheetWidth, new Rectangle(31, 1, 13, 13));
// Size of the parrot icon
subTextureColors = new Color[9 * 14];
// Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(155, 148, 9, 14));
Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(146, 149, 9, 14));
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(54, 4, 9, 14),
true);
Tools.SetSubTexture(subTextureColors, _weatherIconColors, WeatherSheetWidth, new Rectangle(72, 4, 9, 14),
true);
_iconSheet.SetData(_weatherIconColors);
}
private void GetWeatherIconSpriteLocation()
{
SetValleyWeatherSprite();
if (HasVisitedIsland())
{
SetIslandWeatherSprite();
}
}
private void SetValleyWeatherSprite()
{
switch (GetWeatherForTomorrow())
{
case Game1.weather_sunny:
case Game1.weather_debris:
case Game1.weather_festival:
case Game1.weather_wedding:
_valleyWeather.IsRainyTomorrow = false;
break;
case Game1.weather_rain:
_valleyWeather.IsRainyTomorrow = true;
_valleyWeather.SpriteLocation = new Rectangle(0, 0, 15, 15);
_valleyWeather.HoverText = _helper.SafeGetString(LanguageKeys.RainNextDay);
break;
case Game1.weather_lightning:
_valleyWeather.IsRainyTomorrow = true;
_valleyWeather.SpriteLocation = new Rectangle(15, 0, 15, 15);
_valleyWeather.HoverText = _helper.SafeGetString(LanguageKeys.ThunderstormNextDay);
break;
case Game1.weather_snow:
_valleyWeather.IsRainyTomorrow = true;
_valleyWeather.SpriteLocation = new Rectangle(30, 0, 15, 15);
_valleyWeather.HoverText = _helper.SafeGetString(LanguageKeys.SnowNextDay);
break;
default:
_valleyWeather.IsRainyTomorrow = false;
break;
}
}
private void SetIslandWeatherSprite()
{
var islandWeather = GetIslandWeatherForTomorrow();
switch (islandWeather)
{
case Game1.weather_rain:
_islandWeather.IsRainyTomorrow = true;
_islandWeather.SpriteLocation = new Rectangle(45, 0, 18, 18);
_islandWeather.HoverText = _helper.SafeGetString(LanguageKeys.IslandRainNextDay);
break;
case Game1.weather_lightning:
_islandWeather.IsRainyTomorrow = true;
_islandWeather.SpriteLocation = new Rectangle(63, 0, 18, 18);
_islandWeather.HoverText = _helper.SafeGetString(LanguageKeys.IslandThunderstormNextDay);
break;
default:
_islandWeather.IsRainyTomorrow = false;
break;
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowRobinBuildingStatusIcon.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowRobinBuildingStatusIcon : IDisposable
{
#region Properties
private bool _IsBuildingInProgress;
private Rectangle? _buildingIconSpriteLocation;
private string _hoverText;
private PerScreen<ClickableTextureComponent> _buildingIcon = new();
private Texture2D _robinIconSheet;
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowRobinBuildingStatusIcon(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showRobinBuildingStatus)
{
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
if (showRobinBuildingStatus)
{
UpdateRobinBuindingStatusData();
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
}
}
#endregion
#region Event subscriptions
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
UpdateRobinBuindingStatusData();
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
// Draw icon
if (!Game1.eventUp && _IsBuildingInProgress && _buildingIconSpriteLocation.HasValue)
{
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
_buildingIcon.Value =
new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
_robinIconSheet,
_buildingIconSpriteLocation.Value,
8 / 3f);
_buildingIcon.Value.draw(Game1.spriteBatch);
}
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// Show text on hover
if (_IsBuildingInProgress && (_buildingIcon.Value?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false) && !String.IsNullOrEmpty(_hoverText))
{
IClickableMenu.drawHoverText(
Game1.spriteBatch,
_hoverText,
Game1.dialogueFont
);
}
}
#endregion
#region Logic
private bool GetRobinMessage(out string hoverText)
{
int remainingDays = Game1.player.daysUntilHouseUpgrade.Value;
if (remainingDays <= 0)
{
Building b = Game1.getFarm().getBuildingUnderConstruction();
if (b is not null)
{
if (b.daysOfConstructionLeft.Value > b.daysUntilUpgrade.Value)
{
hoverText = String.Format(_helper.SafeGetString(LanguageKeys.RobinBuildingStatus), b.daysOfConstructionLeft.Value);
return true;
}
else
{
// Add another translation string for this?
hoverText = String.Format(_helper.SafeGetString(LanguageKeys.RobinBuildingStatus), b.daysUntilUpgrade.Value);
return true;
}
}
else
{
hoverText = String.Empty;
return false;
}
}
hoverText = String.Format(_helper.SafeGetString(LanguageKeys.RobinHouseUpgradeStatus), remainingDays);
return true;
}
private void UpdateRobinBuindingStatusData()
{
if (GetRobinMessage(out _hoverText))
{
_IsBuildingInProgress = true;
FindRobinSpritesheet();
}
else
{
_IsBuildingInProgress = false;
}
}
private void FindRobinSpritesheet()
{
Texture2D? foundTexture = Game1.getCharacterFromName("Robin")?.Sprite?.Texture;
if (foundTexture != null)
{
_robinIconSheet = foundTexture;
}
else
{
ModEntry.MonitorObject.Log($"{this.GetType().Name}: Could not find Robin spritesheet.", LogLevel.Warn);
}
if (_robinIconSheet == null)
{
ModEntry.MonitorObject.Log($"{this.GetType().Name}: Could not find Robin spritesheet.", LogLevel.Warn);
}
_buildingIconSpriteLocation = new Rectangle(0, 195 + 1, 15, 15 - 1); // 1px edits for better alignment with other icons
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowSeasonalBerry.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowSeasonalBerry : IDisposable
{
#region Properties
private Rectangle? _berrySpriteLocation;
private float _spriteScale = 8 / 3f;
private string _hoverText;
private ClickableTextureComponent _berryIcon;
private readonly IModHelper _helper;
private bool Enabled { get; set; }
private bool ShowHazelnut { get; set; }
#endregion
#region Lifecycle
public ShowSeasonalBerry(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showSeasonalBerry)
{
Enabled = showSeasonalBerry;
_berrySpriteLocation = null;
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
if (showSeasonalBerry)
{
UpdateBerryForDay();
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
}
}
public void ToggleHazelnutOption(bool showHazelnut)
{
ShowHazelnut = showHazelnut;
ToggleOption(Enabled);
}
#endregion
#region Event subscriptions
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
UpdateBerryForDay();
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
// Draw icon
if (Game1.eventUp || !_berrySpriteLocation.HasValue) return;
var iconPosition = IconHandler.Handler.GetNewIconPosition();
_berryIcon =
new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
Game1.objectSpriteSheet,
_berrySpriteLocation.Value,
_spriteScale
);
_berryIcon.draw(Game1.spriteBatch);
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// Show text on hover
var hasMouse = _berryIcon?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false;
var hasText = !string.IsNullOrEmpty(_hoverText);
if (_berrySpriteLocation.HasValue && hasMouse && hasText)
{
IClickableMenu.drawHoverText(
Game1.spriteBatch,
_hoverText,
Game1.dialogueFont
);
}
}
#endregion
#region Logic
private void UpdateBerryForDay()
{
var season = Game1.currentSeason;
var day = Game1.dayOfMonth;
switch (season)
{
case "spring" when day is >= 15 and <= 18:
_berrySpriteLocation = new Rectangle(128, 193, 15, 15);
_hoverText = _helper.SafeGetString(LanguageKeys.CanFindSalmonberry);
_spriteScale = 8 / 3f;
break;
case "fall" when day is >= 8 and <= 11:
_berrySpriteLocation = new Rectangle(32, 272, 16, 16);
_hoverText = _helper.SafeGetString(LanguageKeys.CanFindBlackberry);
_spriteScale = 5 / 2f;
break;
case "fall" when day >= 15 && ShowHazelnut:
_berrySpriteLocation = new Rectangle(1, 274, 14, 14);
_hoverText = _helper.SafeGetString(LanguageKeys.CanFindHazelnut);
_spriteScale = 20 / 7f;
break;
default:
_berrySpriteLocation = null;
break;
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowTodaysGifts.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Linq;
using System.Reflection;
namespace UIInfoSuite2.UIElements
{
internal class ShowTodaysGifts : IDisposable
{
#region Properties
private string[] _friendNames;
private SocialPage _socialPage;
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowTodaysGifts(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showTodaysGift)
{
_helper.Events.Display.MenuChanged -= OnMenuChanged;
_helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu;
if (showTodaysGift)
{
_helper.Events.Display.MenuChanged += OnMenuChanged;
_helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu;
}
}
#endregion
#region Event subscriptions
private void OnRenderedActiveMenu(object sender, RenderedActiveMenuEventArgs e)
{
if (_socialPage == null)
{
ExtendMenuIfNeeded();
return;
}
if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 2)
{
DrawTodaysGifts();
string hoverText = gameMenu.hoverText;
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText,
Game1.smallFont);
}
}
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
ExtendMenuIfNeeded();
}
#endregion
#region Logic
private void ExtendMenuIfNeeded()
{
if (Game1.activeClickableMenu is GameMenu gameMenu)
{
foreach (var menu in gameMenu.pages)
{
if (menu is SocialPage page)
{
_socialPage = page;
_friendNames = _socialPage.names
.Select(name => name.ToString())
.ToArray();
break;
}
}
}
}
private void DrawTodaysGifts()
{
int slotPosition = (int)typeof(SocialPage)
.GetField(
"slotPosition",
BindingFlags.Instance
| BindingFlags.NonPublic)
.GetValue(_socialPage);
int yOffset = 25;
for (int i = slotPosition; i < slotPosition + 5 && i < _friendNames.Length; ++i)
{
int yPosition = Game1.activeClickableMenu.yPositionOnScreen + 130 + yOffset;
yOffset += 112;
string nextName = _friendNames[i];
if (_socialPage.getFriendship(nextName).GiftsToday != 0 && _socialPage.getFriendship(nextName).GiftsThisWeek < 2)
{
Game1.spriteBatch.Draw(
Game1.mouseCursors,
new Vector2(_socialPage.xPositionOnScreen + 384 + 296 + 4, yPosition + 6),
new Rectangle?(new Rectangle(106, 442, 9, 9)),
Color.LightGray, 0.0f, Vector2.Zero, 3f, SpriteEffects.None, 0.22f
);
}
}
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowToolUpgradeStatus.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using System;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
internal class ShowToolUpgradeStatus : IDisposable
{
#region Properties
private readonly PerScreen<Rectangle> _toolTexturePosition = new();
private readonly PerScreen<string> _hoverText = new();
private readonly PerScreen<Tool> _toolBeingUpgraded = new();
private readonly PerScreen<ClickableTextureComponent> _toolUpgradeIcon = new();
private readonly IModHelper _helper;
#endregion
#region Life cycle
public ShowToolUpgradeStatus(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
_toolBeingUpgraded.Value = null;
}
public void ToggleOption(bool showToolUpgradeStatus)
{
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.GameLoop.UpdateTicked -= OnUpdateTicked;
if (showToolUpgradeStatus)
{
UpdateToolInfo();
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.GameLoop.UpdateTicked += OnUpdateTicked;
}
}
#endregion
#region Event subscriptions
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (e.IsOneSecond && _toolBeingUpgraded.Value != Game1.player.toolBeingUpgraded.Value)
UpdateToolInfo();
}
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
UpdateToolInfo();
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
// Draw icon
if (!Game1.eventUp && _toolBeingUpgraded.Value != null)
{
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
_toolUpgradeIcon.Value =
new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
Game1.toolSpriteSheet,
_toolTexturePosition.Value,
2.5f);
// Special case for the Love of Cooking mod's frying pan
if (_toolBeingUpgraded.Value.GetType().FullName == "LoveOfCooking.Objects.CookingTool")
{
try
{
_toolBeingUpgraded.Value.drawInMenu(e.SpriteBatch, iconPosition.ToVector2() - new Vector2(16) / 2, 2.5f / Game1.pixelZoom);
}
catch (Exception ex)
{
ModEntry.MonitorObject.LogOnce("An error occured while drawing the frying pan icon from the Love of Cooking mod.", LogLevel.Error);
ModEntry.MonitorObject.Log(ex.ToString());
}
}
else
{
_toolUpgradeIcon.Value.draw(e.SpriteBatch);
}
}
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// Show text on hover
if (_toolBeingUpgraded.Value != null && (_toolUpgradeIcon.Value?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false))
{
IClickableMenu.drawHoverText(
Game1.spriteBatch,
_hoverText.Value,
Game1.dialogueFont
);
}
}
#endregion
#region Logic
private void UpdateToolInfo()
{
if (Game1.player.toolBeingUpgraded.Value == null)
{
_toolBeingUpgraded.Value = null;
return;
}
Tool toolBeingUpgraded = _toolBeingUpgraded.Value = Game1.player.toolBeingUpgraded.Value;
Rectangle toolTexturePosition = new Rectangle();
if (toolBeingUpgraded is StardewValley.Tools.WateringCan)
{
toolTexturePosition.X = 32;
toolTexturePosition.Y = 228;
toolTexturePosition.Width = 16;
toolTexturePosition.Height = 11;
toolTexturePosition.X += (111 * toolBeingUpgraded.UpgradeLevel);
}
else
{
toolTexturePosition.Width = 16;
toolTexturePosition.Height = 16;
if (toolBeingUpgraded is StardewValley.Tools.Hoe)
{
toolTexturePosition.X = 81;
toolTexturePosition.Y = 31;
toolTexturePosition.X += (111 * toolBeingUpgraded.UpgradeLevel);
}
else if (toolBeingUpgraded is StardewValley.Tools.Pickaxe)
{
toolTexturePosition.X = 81;
toolTexturePosition.Y = 31 + 64;
toolTexturePosition.X += (111 * toolBeingUpgraded.UpgradeLevel);
}
else if (toolBeingUpgraded is StardewValley.Tools.Axe)
{
toolTexturePosition.X = 81;
toolTexturePosition.Y = 31 + 64 + 64;
toolTexturePosition.X += (111 * toolBeingUpgraded.UpgradeLevel);
}
else if (toolBeingUpgraded is StardewValley.Tools.GenericTool)
{
toolTexturePosition.X = 208;
toolTexturePosition.Y = 0;
toolTexturePosition.X += (16 * toolBeingUpgraded.UpgradeLevel);
}
}
if (toolTexturePosition.X > Game1.toolSpriteSheet.Width)
{
toolTexturePosition.Y += 32;
toolTexturePosition.X -= 333;
}
if (Game1.player.daysLeftForToolUpgrade.Value > 0)
{
_hoverText.Value = string.Format(_helper.SafeGetString(LanguageKeys.DaysUntilToolIsUpgraded),
Game1.player.daysLeftForToolUpgrade.Value, toolBeingUpgraded.DisplayName);
}
else
{
_hoverText.Value = string.Format(_helper.SafeGetString(LanguageKeys.ToolIsFinishedBeingUpgraded),
toolBeingUpgraded.DisplayName);
}
_toolTexturePosition.Value = toolTexturePosition;
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowTravelingMerchant.cs
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Objects;
using System;
using System.Linq;
using UIInfoSuite2.Infrastructure;
using UIInfoSuite2.Infrastructure.Extensions;
namespace UIInfoSuite2.UIElements
{
public class ShowTravelingMerchant : IDisposable
{
#region Properties
private bool _travelingMerchantIsHere;
private bool _travelingMerchantIsVisited;
private ClickableTextureComponent _travelingMerchantIcon;
private bool Enabled { get; set; }
private bool HideWhenVisited { get; set; }
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowTravelingMerchant(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showTravelingMerchant)
{
Enabled = showTravelingMerchant;
_helper.Events.Display.RenderingHud -= OnRenderingHud;
_helper.Events.Display.RenderedHud -= OnRenderedHud;
_helper.Events.GameLoop.DayStarted -= OnDayStarted;
_helper.Events.Display.MenuChanged -= OnMenuChanged;
if (showTravelingMerchant)
{
UpdateTravelingMerchant();
_helper.Events.Display.RenderingHud += OnRenderingHud;
_helper.Events.Display.RenderedHud += OnRenderedHud;
_helper.Events.GameLoop.DayStarted += OnDayStarted;
_helper.Events.Display.MenuChanged += OnMenuChanged;
}
}
public void ToggleHideWhenVisitedOption(bool hideWhenVisited)
{
HideWhenVisited = hideWhenVisited;
ToggleOption(Enabled);
}
#endregion
#region Event subscriptions
private void OnDayStarted(object sender, EventArgs e)
{
UpdateTravelingMerchant();
}
private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{
if (e.NewMenu is ShopMenu menu && menu.forSale.Any(s => !(s is Hat)) && Game1.currentLocation.Name == "Forest")
{
_travelingMerchantIsVisited = true;
}
}
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
{
// Draw icon
if (!Game1.eventUp && ShouldDrawIcon())
{
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
_travelingMerchantIcon =
new ClickableTextureComponent(
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
Game1.mouseCursors,
new Rectangle(192, 1411, 20, 20),
2f);
_travelingMerchantIcon.draw(Game1.spriteBatch);
}
}
private void OnRenderedHud(object sender, RenderedHudEventArgs e)
{
// Show text on hover
if (ShouldDrawIcon() && (_travelingMerchantIcon?.containsPoint(Game1.getMouseX(), Game1.getMouseY()) ?? false))
{
string hoverText = _helper.SafeGetString(LanguageKeys.TravelingMerchantIsInTown);
IClickableMenu.drawHoverText(
Game1.spriteBatch,
hoverText,
Game1.dialogueFont
);
}
}
#endregion
#region Logic
private void UpdateTravelingMerchant()
{
int dayOfWeek = Game1.dayOfMonth % 7;
_travelingMerchantIsHere = dayOfWeek == 0 || dayOfWeek == 5;
_travelingMerchantIsVisited = false;
}
private bool ShouldDrawIcon()
{
return _travelingMerchantIsHere && (!_travelingMerchantIsVisited || !HideWhenVisited);
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIElements/ShowWhenAnimalNeedsPet.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Netcode;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Characters;
using StardewValley.Locations;
using StardewValley.Network;
using System;
using System.Linq;
namespace UIInfoSuite2.UIElements
{
internal class ShowWhenAnimalNeedsPet : IDisposable
{
#region Properties
private readonly PerScreen<float> _yMovementPerDraw = new();
private readonly PerScreen<float> _alpha = new();
private bool Enabled { get; set; }
private bool HideOnMaxFriendship { get; set; }
private readonly IModHelper _helper;
#endregion
#region Lifecycle
public ShowWhenAnimalNeedsPet(IModHelper helper)
{
_helper = helper;
}
public void Dispose()
{
ToggleOption(false);
}
public void ToggleOption(bool showWhenAnimalNeedsPet)
{
Enabled = showWhenAnimalNeedsPet;
_helper.Events.Player.Warped -= OnWarped;
_helper.Events.Display.RenderingHud -= OnRenderingHud_DrawAnimalHasProduct;
_helper.Events.Display.RenderingHud -= OnRenderingHud_DrawNeedsPetTooltip;
_helper.Events.GameLoop.UpdateTicked -= UpdateTicked;
if (showWhenAnimalNeedsPet)
{
_helper.Events.Player.Warped += OnWarped;
_helper.Events.Display.RenderingHud += OnRenderingHud_DrawAnimalHasProduct;
_helper.Events.Display.RenderingHud += OnRenderingHud_DrawNeedsPetTooltip;
_helper.Events.GameLoop.UpdateTicked += UpdateTicked;
}
}
public void ToggleDisableOnMaxFriendshipOption(bool hideOnMaxFriendship)
{
HideOnMaxFriendship = hideOnMaxFriendship;
ToggleOption(Enabled);
}
#endregion
#region Event subscriptions
private void OnWarped(object sender, WarpedEventArgs e)
{
}
private void OnRenderingHud_DrawNeedsPetTooltip(object sender, RenderingHudEventArgs e)
{
if (!Game1.eventUp && Game1.activeClickableMenu == null && (Game1.currentLocation is AnimalHouse || Game1.currentLocation is Farm || Game1.currentLocation is FarmHouse))
{
DrawIconForFarmAnimals();
DrawIconForPets();
}
}
private void OnRenderingHud_DrawAnimalHasProduct(object sender, RenderingHudEventArgs e)
{
if (!Game1.eventUp && Game1.activeClickableMenu == null && (Game1.currentLocation is AnimalHouse || Game1.currentLocation is Farm))
{
DrawAnimalHasProduct();
}
}
private void UpdateTicked(object sender, UpdateTickedEventArgs e)
{
if (Game1.eventUp || Game1.activeClickableMenu != null || !(Game1.currentLocation is AnimalHouse || Game1.currentLocation is Farm || Game1.currentLocation is FarmHouse))
return;
float sine = (float)Math.Sin(e.Ticks / 20.0);
_yMovementPerDraw.Value = -6f + 6f * sine;
_alpha.Value = 0.8f + 0.2f * sine;
}
#endregion
#region Logic
private void DrawAnimalHasProduct()
{
var animalsInCurrentLocation = GetAnimalsInCurrentLocation();
if (animalsInCurrentLocation != null)
{
foreach (var animal in animalsInCurrentLocation.Pairs)
{
if (animal.Value.harvestType.Value != FarmAnimal.layHarvestType &&
!animal.Value.IsEmoting &&
animal.Value.currentProduce.Value != 430 &&
animal.Value.currentProduce.Value
> 0 &&
animal.Value.age.Value >= animal.Value.ageWhenMature.Value)
{
Vector2 positionAboveAnimal = GetPetPositionAboveAnimal(animal.Value);
positionAboveAnimal.Y += (float)(Math.Sin(Game1.currentGameTime.TotalGameTime.TotalMilliseconds / 300.0 + animal.Value.Name.GetHashCode()) * 5.0);
Game1.spriteBatch.Draw(
Game1.emoteSpriteSheet,
Utility.ModifyCoordinatesForUIScale(new Vector2(positionAboveAnimal.X + 14f, positionAboveAnimal.Y)),
new Rectangle(3 * (Game1.tileSize / 4) % Game1.emoteSpriteSheet.Width, 3 * (Game1.tileSize / 4) / Game1.emoteSpriteSheet.Width * (Game1.tileSize / 4), Game1.tileSize / 4, Game1.tileSize / 4),
Color.White * 0.9f,
0.0f,
Vector2.Zero,
4f,
SpriteEffects.None,
1f);
Rectangle sourceRectangle = GameLocation.getSourceRectForObject(animal.Value.currentProduce.Value);
Game1.spriteBatch.Draw(
Game1.objectSpriteSheet,
Utility.ModifyCoordinatesForUIScale(new Vector2(positionAboveAnimal.X + 28f, positionAboveAnimal.Y + 8f)),
sourceRectangle,
Color.White * 0.9f,
0.0f,
Vector2.Zero,
2.2f,
SpriteEffects.None,
1f);
}
}
}
}
private void DrawIconForFarmAnimals()
{
var animalsInCurrentLocation = GetAnimalsInCurrentLocation();
if (animalsInCurrentLocation != null)
{
foreach (var animal in animalsInCurrentLocation.Pairs)
{
if (!animal.Value.IsEmoting &&
!animal.Value.wasPet.Value &&
(animal.Value.friendshipTowardFarmer.Value < 1000 || !HideOnMaxFriendship))
{
Vector2 positionAboveAnimal = GetPetPositionAboveAnimal(animal.Value);
string animalType = animal.Value.type.Value.ToLower();
if (animalType.Contains("cow") ||
animalType.Contains("sheep") ||
animalType.Contains("goat") ||
animalType.Contains("pig"))
{
positionAboveAnimal.X += 50f;
positionAboveAnimal.Y += 50f;
}
Game1.spriteBatch.Draw(
Game1.mouseCursors,
Utility.ModifyCoordinatesForUIScale(new Vector2(positionAboveAnimal.X, positionAboveAnimal.Y + _yMovementPerDraw.Value)),
new Rectangle(32, 0, 16, 16),
Color.White * _alpha.Value,
0.0f,
Vector2.Zero,
4f,
SpriteEffects.None,
1f);
}
}
}
}
private void DrawIconForPets()
{
foreach (var character in Game1.currentLocation.characters)
{
if (character is Pet pet &&
!pet.lastPetDay.Values.Any(day => day == Game1.Date.TotalDays)
&& (pet.friendshipTowardFarmer.Value < 1000 || !HideOnMaxFriendship))
{
Vector2 positionAboveAnimal = GetPetPositionAboveAnimal(character);
positionAboveAnimal.X += 50f;
positionAboveAnimal.Y -= 20f;
Game1.spriteBatch.Draw(
Game1.mouseCursors,
Utility.ModifyCoordinatesForUIScale(new Vector2(positionAboveAnimal.X, positionAboveAnimal.Y + _yMovementPerDraw.Value)),
new Rectangle(32, 0, 16, 16),
Color.White * _alpha.Value,
0.0f,
Vector2.Zero,
4f,
SpriteEffects.None,
1f);
}
}
}
private Vector2 GetPetPositionAboveAnimal(Character animal)
{
return new Vector2(Game1.viewport.Width <= Game1.currentLocation.map.DisplayWidth ? animal.position.X - Game1.viewport.X + 16 : animal.position.X + ((Game1.viewport.Width - Game1.currentLocation.map.DisplayWidth) / 2 + 18),
Game1.viewport.Height <= Game1.currentLocation.map.DisplayHeight ? animal.position.Y - Game1.viewport.Y - 34 : animal.position.Y + ((Game1.viewport.Height - Game1.currentLocation.map.DisplayHeight) / 2 - 50));
}
private NetLongDictionary<FarmAnimal, NetRef<FarmAnimal>> GetAnimalsInCurrentLocation()
{
NetLongDictionary<FarmAnimal, NetRef<FarmAnimal>> animals = null;
if (Game1.currentLocation is AnimalHouse)
{
animals = (Game1.currentLocation as AnimalHouse).animals;
}
else if (Game1.currentLocation is Farm)
{
animals = (Game1.currentLocation as Farm).animals;
}
return animals;
}
#endregion
}
}
UIInfoSuite2-master/UIInfoSuite2/UIInfoSuite2.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>2.2.9</Version>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<Import Project="UIInfoSuite2.csproj.local" Condition="Exists('UIInfoSuite2.csproj.local')" />
<ItemGroup>
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\LevelUp license.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Assets\LevelUp.wav">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\de.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\default.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\es.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\fr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\hu.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\ja.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\ko.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\pl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\pt.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\ru.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\th.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\tr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\uk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="i18n\zh.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
UIInfoSuite2-master/UIInfoSuite2/i18n/de.json
{
//GUI - Billboard
"Billboard": "Schwarzes Brett",
"Calendar": "Kalender",
//GUI - Seed shop
"HarvestPrice": "Erntewert",
//Production
"Days": "Tage",
"Hours": "Stunden",
"Minutes": "Minuten",
//Harvest
"DaysToMature": "Tage bis gereift",
"ReadyToHarvest": "Bereit zum Ernten!",
//Display icons - General
"TodaysRecipe": "Heutiges Rezept: ",
"TravelingMerchantIsInTown": "Der reisende Händler ist im Dorf!",
"RainNextDay": "Es wird morgen regnen",
"ThunderstormNextDay": "Es wird morgen ein Gewitter geben",
"SnowNextDay": "Es wird morgen schneien",
"IslandRainNextDay": "Es wird morgen regnen",
"IslandThunderstormNextDay": "Es wird morgen ein Gewitter geben",
"DaysUntilToolIsUpgraded": "{0} Tage bis {1} fertig verbessert wurde",
"ToolIsFinishedBeingUpgraded": "{0} ist fertig!",
"NpcBirthday": "{0}'s Geburtstag",
"RobinBuildingStatus": "Robin wird in {0} Tage(n) mit dem Bau fertig sein",
// Display icons - Foragables
"CanFindSalmonberry": "Du kannst heute Lachsbeeren finden",
"CanFindBlackberry": "Du kannst heute Brombeeren finden",
"CanFindHazelnut": "Du kannst heute Haselnüsse auf Bäumen finden",
//Display icons - Luck
"DailyLuckValue": "Dein heutiges Glück ist {0}",
"FeelingLucky": "Heute ist dein absoluter Glückstag!",
"LuckyButNotTooLucky": "Du fühlst dich glücklich ... aber nicht zu glücklich",
"NotFeelingLuckyAtAll": "Du fühlst dich heute gar nicht glücklich",
"MaybeStayHome": "Vielleicht solltest du heute zu Hause bleiben",
"LuckStatus1": "Heute ist dein absoluter Glückstag!",
"LuckStatus2": "Heute hast du das Glück auf deiner Seite",
"LuckStatus3": "Der Tag liegt in deinen Händen",
"LuckStatus4": "Fortuna's Laune ist heute absolut neutral",
"LuckStatus5": "Das Glück ist heute nicht auf deiner Seite",
"LuckStatus6": "Vielleicht solltest du heute zu Hause bleiben",
//Settings - General
"ShowLevelUpAnimation": "Zeige Aufstiegsanimation",
"ShowExperienceBar": "Zeige Erfahrungsleiste",
"AllowExperienceBarToFadeOut": "Erlaube verblassende Erfahrungsleiste",
"ShowExperienceGain": "Zeige Erfahrungsgewinn",
"ShowLocationOfTownsPeople": "Zeige Bewohner auf der Karte",
"ShowAnimalsNeedPets": "Zeige wenn Tiere gestreichelt werden müssen",
"HideAnimalPetOnMaxFriendship": "Verstecke bei maximaler Zuneigung",
"ShowCropAndBarrelTooltip": "Zeige Ernte- und Reifezeiten",
"ShowItemEffectRanges": "Zeige Vogelscheuche und Sprinkler-Bereich",
"ShowExtraItemInformation": "Zeige detaillierte Info für Gegenstände",
"ShowHarvestPricesInShop": "Zeige Erntewerte",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Zeige Schaltfläche für Kalender und schwarzes Brett",
"ShowHeartFills": "Zeige detaillierte Herzfüllungen",
"ShowTodaysGifts": "Zeige heute bereits vergebene Geschenke",
//Settings - Show icons
"ShowBirthdayIcon": "Zeige Geburtstagssymbol",
"HideBirthdayIfFullFriendShip": "Verstecke bei maximaler Zuneigung",
"ShowTravelingMerchant": "Zeige reisenden Händler",
"HideMerchantWhenVisited": "Verstecke wenn besucht",
"ShowRainyDay": "Zeige regnerische Tage",
"ShowWhenNewRecipesAreAvailable": "Zeige wenn neue Rezepte verfügbar sind",
"ShowToolUpgradeStatus": "Zeige Werkzeugverbesserungsfortschritt",
"ShowRobinBuildingStatusIcon": "Zeige Robins Bau-Status",
"ShowLuckIcon": "Zeige Glücksymbol",
"ShowExactValue": "Zeige exakten Wert",
"ShowSeasonalBerry": "Zeige saisonale Früchte",
"ShowSeasonalBerryHazelnut": "Zeige Haselnüsse auf Bäumen",
//Others
"LevelUp": "Level aufgestiegen"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/default.json
{
//GUI - Billboard
"Billboard": "Billboard",
"Calendar": "Calendar",
//GUI - Seed shop
"HarvestPrice": "Harvest price",
//Production
"Days": "days",
"Hours": "hours",
"Minutes": "minutes",
//Harvest
"DaysToMature": "days to mature",
"ReadyToHarvest": "Ready To Harvest!",
//Display icons - General
"TodaysRecipe": "Today's Recipe: ",
"TravelingMerchantIsInTown": "Traveling merchant is in town!",
"RainNextDay": "There will be rain tomorrow",
"ThunderstormNextDay": "There will be a thunderstorm tomorrow",
"SnowNextDay": "There will be snow tomorrow",
"IslandRainNextDay": "There will be rain on the island tomorrow",
"IslandThunderstormNextDay": "There will be a thunderstorm on the island tomorrow",
"DaysUntilToolIsUpgraded": "{0} days until {1} is finished being upgraded",
"ToolIsFinishedBeingUpgraded": "{0} is finished!",
"NpcBirthday": "{0}'s birthday",
"RobinBuildingStatus": "Robin will finish your new building in {0} day(s)",
"RobinHouseUpgradeStatus": "Robin will finish upgrading your house in {0} day(s)",
// Display icons - Foragables
"CanFindSalmonberry": "You can find Salmonberries today",
"CanFindBlackberry": "You can find Blackberries today",
"CanFindHazelnut": "You can find Hazelnuts on trees today",
//Display icons - Luck
"DailyLuckValue": "Your daily luck is {0}",
"FeelingLucky": "You're feelin' lucky!!",
"LuckyButNotTooLucky": "Feelin' lucky... but not too lucky",
"NotFeelingLuckyAtAll": "You're not feeling lucky at all today...",
"MaybeStayHome": "Maybe you should stay home today...",
"LuckStatus1": "Feelin' lucky!",
"LuckStatus2": "Feelin' a little lucky today!",
"LuckStatus3": "The day is in your hands",
"LuckStatus4": "The spirits feel absolutely neutral today",
"LuckStatus5": "Luck will not be on your side today",
"LuckStatus6": "Maybe you should stay home today...",
//Settings - General
"ShowLevelUpAnimation": "Show level up animation",
"ShowExperienceBar": "Show experience bar",
"AllowExperienceBarToFadeOut": "Allow experience bar to fade out",
"ShowExperienceGain": "Show experience gain",
"ShowLocationOfTownsPeople": "Show townspeople on map",
"ShowAnimalsNeedPets": "Show when animals need pets",
"HideAnimalPetOnMaxFriendship": "Hide on max friendship",
"ShowCropAndBarrelTooltip": "Show crop and barrel times",
"ShowItemEffectRanges": "Show scarecrow and sprinkler range",
"ShowExtraItemInformation": "Show item hover information",
"ShowHarvestPricesInShop": "Show shop harvest prices",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Show calendar/billboard button",
"ShowHeartFills": "Show heart fills",
"ShowTodaysGifts": "Show gifts given today",
//Settings - Show icons
"ShowBirthdayIcon": "Show Birthday icon",
"HideBirthdayIfFullFriendShip": "Hide on max friendship",
"ShowTravelingMerchant": "Show Traveling Merchant",
"HideMerchantWhenVisited": "Hide when visited",
"ShowRainyDay": "Show rainy days",
"ShowWhenNewRecipesAreAvailable": "Show when new recipes are available",
"ShowToolUpgradeStatus": "Show tool upgrade status",
"ShowRobinBuildingStatusIcon": "Show Robin building status",
"ShowLuckIcon": "Show luck icon",
"ShowExactValue": "Show exact value",
"ShowSeasonalBerry": "Show seasonal forageables",
"ShowSeasonalBerryHazelnut": "Show hazelnuts on trees",
//Others
"LevelUp": "Level Up"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/es.json
{
//GUI - Billboard
"Billboard": "Tablón de anuncios",
"Calendar": "Calendario",
//GUI - Seed shop
"HarvestPrice": "Precio de la cosecha",
//Production
"Days": "días",
"Hours": "horas",
"Minutes": "minutos",
//Harvest
"DaysToMature": "días para madurar",
"ReadyToHarvest": "¡Listo para cosechar!",
//Display icons - General
"TodaysRecipe": "Receta de hoy: ",
"TravelingMerchantIsInTown": "¡El comerciante ambulante está en la zona!",
"RainNextDay": "Mañana lloverá",
"ThunderstormNextDay": "Mañana habrá tormenta eléctrica",
"SnowNextDay": "Mañana nevará",
"IslandRainNextDay": "Mañana lloverá",
"IslandThunderstormNextDay": "Mañana habrá tormenta eléctrica",
"DaysUntilToolIsUpgraded": "{0} días hasta que {1} termine de actualizarse",
"ToolIsFinishedBeingUpgraded": "¡{0} está terminado!",
"NpcBirthday": "Cumpleaños de {0}",
"RobinBuildingStatus": "Robin terminará tu nuevo edificio en {0} día(s)",
// Display
icons - Foragables
"CanFindSalmonberry": "Hoy puedes encontrar Frambuesas",
"CanFindBlackberry": "Hoy puedes encontrar Moras",
"CanFindHazelnut": "Hoy puedes encontrar Avellanas en los árboles",
//Display icons - Luck
"DailyLuckValue": "Tu suerte diaria es {0}",
"FeelingLucky": "¡Tienes suerte!",
"LuckyButNotTooLucky": "Te sientes con suerte... aunque no demasiada",
"NotFeelingLuckyAtAll": "No te sientes afortunado en absoluto...",
"MaybeStayHome": "Tal vez deberías quedarte en casa hoy...",
"LuckStatus1": "¡Tienes suerte!",
"LuckStatus2": "Hoy me siento un poco afortunado",
"LuckStatus3": "El día está en tus manos",
"LuckStatus4": "Los espíritus se sienten absolutamente neutrales hoy",
"LuckStatus5": "La suerte no estará de tu lado hoy",
"LuckStatus6": "Tal vez deberías quedarte en casa hoy...",
//Settings - General
"ShowLevelUpAnimation": "Mostrar animación de subida de nivel",
"ShowExperienceBar": "Mostrar barra de experiencia",
"AllowExperienceBarToFadeOut": "Permitir que la barra de experiencia se desvanezca",
"ShowExperienceGain": "Mostrar ganancia de experiencia",
"ShowLocationOfTownsPeople": "Mostrar a la gente del pueblo en el mapa",
"ShowAnimalsNeedPets": "Mostrar si los animales necesitan caricias",
"HideAnimalPetOnMaxFriendship": "Ocultar con la amistad al máximo",
"ShowCropAndBarrelTooltip": "Mostrar tiempo restante de cultivos y barriles",
"ShowItemEffectRanges": "Mostrar rango de espantapájaros y aspersores",
"ShowExtraItemInformation": "Mostrar información extra de objetos",
"ShowHarvestPricesInShop": "Mostrar los precios de cosechas de la tienda",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Mostrar el botón de calendario/tablón",
"ShowHeartFills": "Ver nivel de amistad detallado",
"ShowTodaysGifts": "Mostrar regalos entregados hoy",
//Settings - Show icons
"ShowBirthdayIcon": "Mostrar icono de cumpleaños",
"HideBirthdayIfFullFriendShip": "Ocultar con la amistad al máximo",
"ShowTravelingMerchant": "Mostrar comerciante ambulante",
"HideMerchantWhenVisited": "Ocultar al ser visitado",
"ShowRainyDay": "Mostrar días lluviosos",
"ShowWhenNewRecipesAreAvailable": "Mostrar cuando hay nuevas recetas disponibles",
"ShowToolUpgradeStatus": "Mostrar el estado de actualización de la herramienta",
"ShowRobinBuildingStatusIcon": "Mostrar el estado de construcción de Robin",
"ShowLuckIcon": "Mostrar icono de suerte",
"ShowExactValue": "Mostrar valor exacto",
"ShowSeasonalBerry": "Mostrar recolectables de temporada",
"ShowSeasonalBerryHazelnut": "Mostrar avellanas en los árboles",
//Others
"LevelUp": "Subida de nivel"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/fr.json
{
//GUI - Billboard
"Billboard": "Tableau d'annonces",
"Calendar": "Calendrier",
//GUI - Seed shop
"HarvestPrice": "Valeur de la récolte",
//Production
"Days": "jours",
"Hours": "heures",
"Minutes": "minutes",
//Harvest
"DaysToMature": "jours pour mûrir",
"ReadyToHarvest": "Prêt à récolter!",
//Display icons - General
"TodaysRecipe": "Recette d'aujourd'hui: ",
"TravelingMerchantIsInTown": "Le marchand itinérant est en ville!",
"RainNextDay": "Il va pleuvoir demain",
"ThunderstormNextDay": "Il va y avoir un orage demain",
"SnowNextDay": "Il va neiger demain",
"IslandRainNextDay": "Il va pleuvoir demain",
"IslandThunderstormNextDay": "Il va y avoir un orage demain",
"DaysUntilToolIsUpgraded": "{0} jours avant que {1} ne termine de s'améliorer",
"ToolIsFinishedBeingUpgraded": "{0} est terminé!",
"NpcBirthday": "Anniversaire de {0}",
"RobinBuildingStatus": "Robin aura fini votre nouveau batiment dans {0} jour(s)",
// Display icons - Foragables
"CanFindSalmonberry": "Vous pouvez trouver des baies rouges aujourd'hui",
"CanFindBlackberry": "Vous pouvez trouver des baies noires aujourd'hui",
"CanFindHazelnut": "Vous pouvez trouver des noisettes sur les arbres aujourd'hui ",
//Display icons - Luck
"DailyLuckValue": "Ta chance du jour est {0}",
"FeelingLucky": "Tu te sens chanceux !!",
"LuckyButNotTooLucky": "Tu te sens chanceux... mais pas trop",
"NotFeelingLuckyAtAll": "Tu ne te sens pas du tout chanceux...",
"MaybeStayHome": "Tu devrais peut etre rester à la maison aujourd'hui...",
"LuckStatus1": "Tu te sens chanceux!",
"LuckStatus2": "Tu te sens un peu chanceux",
"LuckStatus3": "La journée est entre tes mains",
"LuckStatus4": "Les esprits sont plutôt neutre aujourd'hui",
"LuckStatus5": "La chance n'est pas avec toi aujourd'hui",
"LuckStatus6": "Tu ferais mieux de rester à la maison aujourd'hui",
//Settings - General
"ShowLevelUpAnimation": "Montrer l'animation de gain de niveau",
"ShowExperienceBar": "Montrer la barre d'exp.",
"AllowExperienceBarToFadeOut": "Permettre à la barre d'exp. de disparaître",
"ShowExperienceGain": "Montrer le gain d'exp.",
"ShowLocationOfTownsPeople": "Montrer les habitants sur la carte",
"ShowAnimalsNeedPets": "Montrer les animaux à câliner",
"HideAnimalPetOnMaxFriendship": "Cacher lorsque l'amitié est à son maximum",
"ShowCropAndBarrelTooltip": "Montrer le temps des récoltes/barils",
"ShowItemEffectRanges": "Montrer portée épouvantails/arroseurs",
"ShowExtraItemInformation": "Montrer informations supp. des objets",
"ShowHarvestPricesInShop": "Montrer le prix des récoles en magasin",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Montrer Calendrier/Tableau d'annonces",
"ShowHeartFills": "Montrer le remplissage des coeurs",
"ShowTodaysGifts": "Montrer les cadeaux offerts aujourd'hui",
//Settings - Show icons
"ShowBirthdayIcon": "Montrer l'icône d'anniversaire",
"HideBirthdayIfFullFriendShip": "Cacher lorsque l'amitié est à son maximum",
"ShowTravelingMerchant": "Montrer le marchand itinérant",
"HideMerchantWhenVisited": "Cacher pendant la visite",
"ShowRainyDay": "Montrer les jours de pluie",
"ShowWhenNewRecipesAreAvailable": "Montrer quand nouvelles recettes disp.",
"ShowToolUpgradeStatus": "Montrer état de l'amélioration des outils",
"ShowRobinBuildingStatusIcon": "Montrer l'avancée du chantier de Robin",
"ShowLuckIcon": "Montrer l'icône de chance",
"ShowExactValue": "Montrer la valeur exacte",
"ShowSeasonalBerry": "Montrer les cultures de saison",
"ShowSeasonalBerryHazelnut": "Montrer les noisettes sur les arbres",
//Others
"LevelUp": "Gain de niveau"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/hu.json
{
//GUI - Billboard
"Billboard": "Hirdetőtábla",
"Calendar": "Naptár",
//GUI - Seed shop
"HarvestPrice": "Betakarítási ár",
//Production
"Days": "nap",
"Hours": "óra",
"Minutes": "perc",
//Harvest
"DaysToMature": "nap a megérésig",
"ReadyToHarvest": "Aratásra kész!",
//Display icons - General
"TodaysRecipe": "Mai recept: ",
"TravelingMerchantIsInTown": "Az utazó kereskedő a városban van!",
"RainNextDay": "Holnap esni fog az eső",
"ThunderstormNextDay": "Holnap vihar lesz",
"SnowNextDay": "Holnap esni fog a hó",
"IslandRainNextDay": "Holnap a szigeten esni fog az eső",
"IslandThunderstormNextDay": "Holnap a szigeten vihar lesz",
"DaysUntilToolIsUpgraded": "{0} nap amíg a(z) {1} fejlesztése elkészül",
"ToolIsFinishedBeingUpgraded": "A(z) {0} fejlesztése be lett fejezve!",
"NpcBirthday": "{0} születésnapja",
"RobinBuildingStatus": "Robin {0} nap múlva fogja befejezni az új épületedet",
// Display icons - Foragables
"CanFindSalmonberry": "Ma díszszedret találhatsz",
"CanFindBlackberry": "Ma szedret találhatsz",
"CanFindHazelnut": "Ma mogyorót találhatsz a fákon",
//Display icons - Luck
"DailyLuckValue": "A napi szerencséd {0}",
"FeelingLucky": "Ma szerencsésnek érzed magad!",
"LuckyButNotTooLucky": "Ma szerencsésnek érzed magad... de nem túl szerencsésnek!",
"NotFeelingLuckyAtAll": "Ma egyáltalán nem érzed magad szerencsésnek!",
"MaybeStayHome": "Talán ma otthon kellene maradnod!",
"LuckStatus1": "Ma szerencsésnek érzed magad!",
"LuckStatus2": "Ma egy kicsit szerencsésnek érzed magad!",
"LuckStatus3": "A nap a te kezedben van!",
"LuckStatus4": "Ma a szellemek teljesen semlegesnek érzik magukat!",
"LuckStatus5": "Ma a szerencse nem a te oldaladon áll!",
"LuckStatus6": "Talán ma otthon kellene maradnod!",
//Settings - General
"ShowLevelUpAnimation": "Szintlépés animáció megjelenítés",
"ShowExperienceBar": "Tapasztalatpont mérő megjelenítés",
"AllowExperienceBarToFadeOut": "Tapasztalatpont mérő elhalványul",
"ShowExperienceGain": "Tapasztalatpont szerzés megjeleníté",
"ShowLocationOfTownsPeople": "Lakók megjelenítése a térképen",
"ShowAnimalsNeedPets": "'Állatokat meg kell simogatni' megjelenítés",
"HideAnimalPetOnMaxFriendship": "Maximális szívszint elrejtés",
"ShowCropAndBarrelTooltip": "Termés és kézműves termék idő megjelenítés",
"ShowItemEffectRanges": "Madárijesztő és öntöző távolság megjelenítés",
"ShowExtraItemInformation": "Tárgy információ megjelenítés",
"ShowHarvestPricesInShop": "Betakarítási ára megjelenítés a boltokban",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Hirdetőtábla/naptár gomb megjelenítés",
"ShowHeartFills": "Szív töltöttség megjelenítés",
"ShowTodaysGifts": "Ma ajándékozott ajándék megjelenítés",
//Settings - Show icons
"ShowBirthdayIcon": "Születésnap ikon megjelenítés",
"HideBirthdayIfFullFriendShip": "Maximális barátság elrejtés",
"ShowTravelingMerchant": "Utazó kereskedő ikon megjelenítés",
"HideMerchantWhenVisited": "Meglátogatáskor elrejtés",
"ShowRainyDay": "Esős nap megjelenítés",
"ShowWhenNewRecipesAreAvailable": "Új elérhető recept megjelenítés",
"ShowToolUpgradeStatus": "Eszközfejlesztési idő megjelenítés",
"ShowRobinBuildingStatusIcon": "Robin építési állapot megjelenítés",
"ShowLuckIcon": "Szerencse ikon megjelenítés",
"ShowExactValue": "Pontos érték megjelenítés",
"ShowSeasonalBerry": "Szezonális takarmánynövény megjelenítés",
"ShowSeasonalBerryHazelnut": "Mogyoró megjelenítés a fákon",
//Others
"LevelUp": "Szintlépés"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/it.json
{
//GUI - Billboard
"Billboard": "Cartellone",
"Calendar": "Calendario",
//GUI - Seed shop
"HarvestPrice": "Prezzo raccolto",
//Production
"Days": "giorni",
"Hours": "ore",
"Minutes": "minuti",
//Harvest
"DaysToMature": "giorni per maturare",
"ReadyToHarvest": "Pronto per essere raccolto!",
//Display icons - General
"TodaysRecipe": "Ricetta di oggi: ",
"TravelingMerchantIsInTown": "Mercante viaggiatore è in città!",
"RainNextDay": "Domani pioverà",
"ThunderstormNextDay": "Ci sarà un temporale domani",
"SnowNextDay": "Domani nevicherà",
"IslandRainNextDay": "Domani pioverà nell'isola",
"IslandThunderstormNextDay": "Ci sarà un temporale domani nell'isola",
"DaysUntilToolIsUpgraded": "{0} giorni finchè {1} sarà migliorato",
"ToolIsFinishedBeingUpgraded": "{0} è completo!",
"NpcBirthday": "Compleanno di {0}",
"RobinBuildingStatus": "Robin completerà il tuo nuovo edificio in {0} giorno(i)",
// Display icons - Foragables
"CanFindSalmonberry": "Oggi puoi trovare Salmonberries",
"CanFindBlackberry": "Oggi puoi trovare More",
"CanFindHazelnut": "Oggi puoi trovare Nocciole sugli alberi",
//Display icons - Luck
"DailyLuckValue": "La tua fortuna giornaliera è {0}",
"FeelingLucky": "Ti senti fortunato!!",
"LuckyButNotTooLucky": "Ti senti abbastanza fortunato",
"NotFeelingLuckyAtAll": "Non ti senti per niente fortunato oggi...",
"MaybeStayHome": "Forse dovresti restare a casa oggi...",
"LuckStatus1": "Ti senti fortunato!",
"LuckStatus2": "Ti senti abbastanza fortunato!",
"LuckStatus3": "Il giorno è nelle tue mani",
"LuckStatus4": "Gli spiriti sono neutrali oggi",
"LuckStatus5": "La fortuna non sarà dalla tua parte oggi",
"LuckStatus6": "Forse dovresti restare a casa oggi...",
//Settings - General
"ShowLevelUpAnimation": "Mostra animazione di nuovo livello",
"ShowExperienceBar": "Mostra barra dell'esperienza",
"AllowExperienceBarToFadeOut": "Permetti alla barra dell'esperienza di scomparire",
"ShowExperienceGain": "Mostra guadagno esperienza",
"ShowLocationOfTownsPeople": "Mostra cittadini nella mappa",
"ShowAnimalsNeedPets": "Mostra quando gli animali vogliono essere accarezzati",
"HideAnimalPetOnMaxFriendship": "Nascondi quando l'amicizia è al massimo",
"ShowCropAndBarrelTooltip": "Mostra tempo raccolto e barili",
"ShowItemEffectRanges": "Mostra area spaventapasseri e spruzzatore",
"ShowExtraItemInformation": "Mostra informazioni oggetto",
"ShowHarvestPricesInShop": "Mostra prezzi raccolto del mercato",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Mostra bottone Calendario/Cartellone",
"ShowHeartFills": "Mostra riempimento cuori",
"ShowTodaysGifts": "Mostra i regali fatti oggi",
//Settings - Show icons
"ShowBirthdayIcon": "Mostra icona compleanno",
"HideBirthdayIfFullFriendShip": "Nascondi quando l'amicizia è al massimo",
"ShowTravelingMerchant": "Mostra mercante viaggiatore",
"HideMerchantWhenVisited": "Nascondi quando visitato",
"ShowRainyDay": "Mostra giorni di pioggia",
"ShowWhenNewRecipesAreAvailable": "Mostra quando sono disponibili nuove ricette",
"ShowToolUpgradeStatus": "Mostra status di miglioramento dell'utensile",
"ShowRobinBuildingStatusIcon": "Mostra status di costruzione di Robin",
"ShowLuckIcon": "Mostra icona fortuna",
"ShowExactValue": "Mostra valore esatto",
"ShowSeasonalBerry": "Mostra foraggiabili di stagione",
"ShowSeasonalBerryHazelnut": "Mostra Nocciole sugli alberi",
//Others
"LevelUp": "Nuovo livello"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/ja.json
{
//GUI - Billboard
"Billboard": "掲示板",
"Calendar": "カレンダー",
//GUI - Seed shop
"HarvestPrice": "収穫価格",
//Production
"Days": "日",
"Hours": "時間",
"Minutes": "分",
//Harvest
"DaysToMature": "日で成熟",
"ReadyToHarvest": "収穫可能!",
//Display icons - General
"TodaysRecipe": "今日のレシピ: ",
"TravelingMerchantIsInTown": "行商人が町にいる!",
"RainNextDay": "明日は雨が降るでしょう",
"ThunderstormNextDay": "明日は雷を伴った雨が降るでしょう",
"SnowNextDay": "明日は雪が降るでしょう",
"IslandRainNextDay": "明日は雨が降るでしょう",
"IslandThunderstormNextDay": "明日は雷を伴った雨が降るでしょう",
"DaysUntilToolIsUpgraded": "{1}のアップグレード完了まで{0}日",
"ToolIsFinishedBeingUpgraded": "{0}のアップグレード完了!",
"NpcBirthday": "{0}の誕生日",
"RobinBuildingStatus": "ロビンは{0}日で建築を完了します",
// Display icons - Foragables
"CanFindSalmonberry": "今日は茂みでサーモンベリーが見つかります",
"CanFindBlackberry": "今日は茂みでブラックベリーが見つかります",
"CanFindHazelnut": "今日はカエデの木でヘーゼルナッツが見つかります",
//Display icons - Luck
"DailyLuckValue": "今日の運は{0}です",
"FeelingLucky": "今日はラッキーな予感!",
"LuckyButNotTooLucky": "幸運を感じる?感じない?",
"NotFeelingLuckyAtAll": "今日は運が味方してくれそうにない...",
"MaybeStayHome": "今日は家で安静にしたほうが良いだろう...",
"LuckStatus1": "今日はラッキーな予感!",
"LuckStatus2": "今日は少しラッキーかも?",
"LuckStatus3": "幸運を感じる?感じない?",
"LuckStatus4": "今日の運勢はどっちつかず",
"LuckStatus5": "今日は運が味方してくれそうにない...",
"LuckStatus6": "今日は家で安静にしたほうが良いだろう...",
//Settings - General
"ShowLevelUpAnimation": "レベルアップアニメーションを表示する",
"ShowExperienceBar": "経験値バーを表示する",
"AllowExperienceBarToFadeOut": "経験値バーをフェードアウトさせる",
"ShowExperienceGain": "入手経験値を表示する",
"ShowLocationOfTownsPeople": "町の人々を地図に表示する",
"ShowAnimalsNeedPets": "動物がふれあいを必要とするアイコンを表示する",
"HideAnimalPetOnMaxFriendship": "友好度が最大の場合は隠す",
"ShowCropAndBarrelTooltip": "作物とタルの時間を表示する",
"ShowItemEffectRanges": "かかしとスプリンクラーの範囲を表示する",
"ShowExtraItemInformation": "アイテムの情報を吹き出し表示する",
"ShowHarvestPricesInShop": "お店で収穫価格を表示する",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "カレンダー/掲示板ボタンを表示する",
"ShowHeartFills": "詳細な友好度を表示する",
"ShowTodaysGifts": "今日贈り物をしたかを表示する",
//Settings - Show icons
"ShowBirthdayIcon": "誕生日アイコンを表示する",
"HideBirthdayIfFullFriendShip": "友好度が最大の場合は隠す",
"ShowTravelingMerchant": "行商人を表示する",
"HideMerchantWhenVisited": "訪問済みの場合は隠す",
"ShowRainyDay": "雨の日を表示する",
"ShowWhenNewRecipesAreAvailable": "新しいレシピが利用可能になったら表示する",
"ShowToolUpgradeStatus": "ツールのアップグレード情報を表示する",
"ShowRobinBuildingStatusIcon": "ロビンの建築状態を表示する",
"ShowLuckIcon": "幸運アイコンを表示する",
"ShowExactValue": "詳細な値を表示する",
"ShowSeasonalBerry": "季節の採取を表示する",
"ShowSeasonalBerryHazelnut": "ヘーゼルナッツを表示する",
//Others
"LevelUp": "レベルアップ"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/ko.json
{
//GUI - Billboard
"Billboard": "게시판",
"Calendar": "달력",
//GUI - Seed shop
"HarvestPrice": "수확 가격",
//Production
"Days": "일",
"Hours": "시간",
"Minutes": "분",
//Harvest
"DaysToMature": "일 남음",
"ReadyToHarvest": "수확 가능!",
//Display icons - General
"TodaysRecipe": "오늘의 레시피: ",
"TravelingMerchantIsInTown": "행상인이 방문 중입니다!",
"RainNextDay": "내일은 비가 올 것입니다",
"ThunderstormNextDay": "내일은 뇌우가 올 것입니다",
"SnowNextDay": "내일은 눈이 올 것입니다",
"IslandRainNextDay": "내일은 비가 올 것입니다",
"IslandThunderstormNextDay": "내일은 뇌우가 올 것입니다",
"DaysUntilToolIsUpgraded": "{1} 완성까지 {0}일 남았습니다",
"ToolIsFinishedBeingUpgraded": "{0}이(가) 완성됐습니다!",
"NpcBirthday": "{0}의 생일입니다!",
"RobinBuildingStatus": "새 건물 완성까지 {0}일 남았습니다",
// Display icons - Foragables
"CanFindSalmonberry": "You can find Salmonberries today", // TODO
"CanFindBlackberry": "You can find Blackberries today", // TODO
"CanFindHazelnut": "You can find Hazelnuts on trees today", // TODO
//Display icons - Luck
"DailyLuckValue": "오늘의 행운 수치: {0}",
"FeelingLucky": "운이 좋은 것 같습니다!!",
"LuckyButNotTooLucky": "운이 좋은 것 같지만... 아주 좋은 것은 아닙니다.",
"NotFeelingLuckyAtAll": "오늘은 운이 좋지 않은것 같습니다...",
"MaybeStayHome": "아마도 오늘은 집에 있어야 할 것 같습니다...",
"LuckStatus1": "오늘은 운이 좋은 것 같다!",
"LuckStatus2": "오늘은 운이 약간 좋은 것 같다!",
"LuckStatus3": "오늘은 스스로 하기에 달려있다",
"LuckStatus4": "오늘은 정령들의 기분이 완벽히 중립적이다",
"LuckStatus5": "오늘은 운이 따라주지 않는 것 같다",
"LuckStatus6": "오늘은 집에 있는 편이 좋을 것 같다...",
//Settings - General
"ShowLevelUpAnimation": "레벨 업 애니메이션 표시",
"ShowExperienceBar": "경험치 바 표시",
"AllowExperienceBarToFadeOut": "경험치 바 자동 숨김",
"ShowExperienceGain": "경험치 획득 표시",
"ShowLocationOfTownsPeople": "지도에 주민 위치 표시",
"ShowAnimalsNeedPets": "쓰다듬이 필요한 동물 표시",
"HideAnimalPetOnMaxFriendship": "친밀도 최대 시 숨김",
"ShowCropAndBarrelTooltip": "작물 및 설비의 남은 시간 표시",
"ShowItemEffectRanges": "허수아비 및 스프링클러 범위 표시",
"ShowExtraItemInformation": "아이템의 추가 정보 표시",
"ShowHarvestPricesInShop": "상점에서 수확 가격 표시",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "소지품 창에 달력 및 게시판 버튼 표시",
"ShowHeartFills": "관계 창에 자세한 하트 단계 표시",
"ShowTodaysGifts": "관계 창에 오늘 선물 보낸 여부 표시",
//Settings - Show icons
"ShowBirthdayIcon": "생일 아이콘 표시",
"HideBirthdayIfFullFriendShip": "친밀도 최대 시 숨김",
"ShowTravelingMerchant": "행상인 아이콘 표시",
"HideMerchantWhenVisited": "방문 후 아이콘 숨김",
"ShowRainyDay": "날씨 아이콘 표시",
"ShowWhenNewRecipesAreAvailable": "새 레시피 획득 가능 시 아이콘 표시",
"ShowToolUpgradeStatus": "도구 업그레이드 현황 아이콘 표시",
"ShowRobinBuildingStatusIcon": "건물 건설 현황 아이콘 표시",
"ShowLuckIcon": "행운 아이콘 표시",
"ShowExactValue": "정확한 수치 표시",
"ShowSeasonalBerry": "Show seasonal forageables", // TODO
"ShowSeasonalBerryHazelnut": "Show hazelnuts on trees", // TODO
//Others
"LevelUp": "레벨 업"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/pl.json
{
//GUI - Billboard
"Billboard": "Tablica Ogłoszeń",
"Calendar": "Kalendarz",
//GUI - Seed shop
"HarvestPrice": "Cena za plon",
//Production
"Days": "dni",
"Hours": "godzin",
"Minutes": "minut",
//Harvest
"DaysToMature": "dni do dojrzałości",
"ReadyToHarvest": "Gotowe do zbioru!",
//Display icons - General
"TodaysRecipe": "Dzisiejszy przepis: ",
"TravelingMerchantIsInTown": "Wędrowny handlarz jest w miasteczku!",
"RainNextDay": "Jutro będzie padać deszcz",
"ThunderstormNextDay": "Jutro będzie burza",
"SnowNextDay": "Jutro będzie padać śnieg",
"IslandRainNextDay": "Jutro na wyspie będzie padać deszcz",
"IslandThunderstormNextDay": "Jutro na wyspie będzie burza",
"DaysUntilToolIsUpgraded": "{0} dni do zakończenia ulepszania: {1}",
"ToolIsFinishedBeingUpgraded": "Ulepszanie zakończone: {0}!",
"NpcBirthday": "{0} ma dziś urodziny",
"RobinBuildingStatus": "Robin ukończy budowę za {0} dni",
// Display icons - Foragables
"CanFindSalmonberry": "Dziś możesz znaleźć Maliny",
"CanFindBlackberry": "Dziś możesz znaleźć Jeżyny",
"CanFindHazelnut": "Dziś na drzewach możesz znaleźć Orzechy Laskowe",
//Display icons - Luck
"DailyLuckValue": "Twoje dzisiejsze szczęście: {0}",
"FeelingLucky": "Czujesz, że dopisuje ci mnóstwo szczęścia!",
"LuckyButNotTooLucky": "Czujesz dopisujące szczęście... lecz nie ma go zbyt wiele",
"NotFeelingLuckyAtAll": "Nie czujesz, aby dopisywało ci dziś szczęście...",
"MaybeStayHome": "Może lepiej zostań dziś w domu...",
"LuckStatus1": "Czujesz, że dopisuje ci mnóstwo szczęścia!",
"LuckStatus2": "Czujesz, że dopisuje ci dziś odrobina szczęścia!",
"LuckStatus3": "Dzień jest w twoich rękach",
"LuckStatus4": "Duchy wydają się dziś zupełnie neutralne",
"LuckStatus5": "Szczęście nie będzie dziś po twojej stronie",
"LuckStatus6": "Może lepiej zostań dziś w domu...",
//Settings - General
"ShowLevelUpAnimation": "Pokaż animację awansu",
"ShowExperienceBar": "Pokaż pasek doświadczenia",
"AllowExperienceBarToFadeOut": "Pozwól na znikanie paska doświadczenia",
"ShowExperienceGain": "Pokaż zdobywane doświadczenie",
"ShowLocationOfTownsPeople": "Pokaż mieszkańców na mapie",
"ShowAnimalsNeedPets": "Pokaż, gdy zwierzęta pragną głaskania",
"HideAnimalPetOnMaxFriendship": "Ukryj na maks. poziomie miłości",
"ShowCropAndBarrelTooltip": "Pokaż czas uprawy i produkcji",
"ShowItemEffectRanges": "Pokaż zasięg strachów i zraszaczy",
"ShowExtraItemInformation": "Pokaż info o przedmiocie po najechaniu",
"ShowHarvestPricesInShop": "Pokaż ceny plonów w sklepie",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Pokaż przycisk kalendarza/tablicy",
"ShowHeartFills": "Pokaż wypełnienie serduszek",
"ShowTodaysGifts": "Pokaż wręczone dziś prezenty",
//Settings - Show icons
"ShowBirthdayIcon": "Pokaż ikonę urodzin",
"HideBirthdayIfFullFriendShip": "Ukryj na maks. poziomie relacji",
"ShowTravelingMerchant": "Pokaż wędrownego handlarza",
"HideMerchantWhenVisited": "Ukryj po odwiedzeniu",
"ShowRainyDay": "Pokaż deszczowe dni",
"ShowWhenNewRecipesAreAvailable": "Pokaż, gdy dostępne są nowe przepisy",
"ShowToolUpgradeStatus": "Pokaż stan ulepszania narzędzia",
"ShowRobinBuildingStatusIcon": "Pokaż stan budowy Robin",
"ShowLuckIcon": "Pokaż ikonę szczęścia",
"ShowExactValue": "Pokaż dokładną wartość",
"ShowSeasonalBerry": "Pokaż sezonowe dary lasu do zebrania",
"ShowSeasonalBerryHazelnut": "Pokaż ikonę orzechów laskowych",
//Others
"LevelUp": "Awans"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/pt.json
{
//GUI - Billboard
"Billboard": "Quadro de avisos",
"Calendar": "Calendário",
//GUI - Seed shop
"HarvestPrice": "Colheita",
//Production
"Days": "dias",
"Hours": "horas",
"Minutes": "minutos",
//Harvest
"DaysToMature": "dias para amadurecer",
"ReadyToHarvest": "pronto para colher!",
//Display icons - General
"TodaysRecipe": "Receita de hoje: ",
"TravelingMerchantIsInTown": "O mercador está na cidade!",
"RainNextDay": "Vai chover amanhã",
"ThunderstormNextDay": "Haverá uma tempestade amanhã",
"SnowNextDay": "Vai nevar amanhã",
"IslandRainNextDay": "Vai chover amanhã",
"IslandThunderstormNextDay": "Haverá uma tempestade amanhã",
"DaysUntilToolIsUpgraded": "{1} ficará pronto em {0} dia(s)",
"ToolIsFinishedBeingUpgraded": "{0} está pronto!",
"NpcBirthday": "Aniversariante do dia: {0}",
"RobinBuildingStatus": "Robin terminará sua construção em {0} dia(s)",
// Display icons - Foragables
"CanFindSalmonberry": "Você pode encontrar amoras-silvestres hoje",
"CanFindBlackberry": "Você pode encontrar amoras hoje",
"CanFindHazelnut": "Você pode encontrar avelãs nas árvores hoje",
//Display icons - Luck
"DailyLuckValue": "Seu número da sorte é {0}",
"FeelingLucky": "Os espíritos estão muito felizes!",
"LuckyButNotTooLucky": "Os espíritos estão de bom humor",
"NotFeelingLuckyAtAll": "Os espíritos estão
meio aborrecidos",
"MaybeStayHome": "Os espíritos estão muito aborrecidos",
"LuckStatus1": "Os espíritos estão muito felizes!",
"LuckStatus2": "Os espíritos estão de bom humor",
"LuckStatus3": "Os espíritos estão neutros",
"LuckStatus4": "Os espíritos estão neutros",
"LuckStatus5": "Os espíritos estão meio aborrecidos",
"LuckStatus6": "Os espíritos estão muito aborrecidos",
//Settings - General
"ShowLevelUpAnimation": "Mostrar animação de Level Up",
"ShowExperienceBar": "Mostrar barra de experiência",
"AllowExperienceBarToFadeOut": "Ocultar barra de exp automaticamente",
"ShowExperienceGain": "Mostrar ganho de experiência",
"ShowLocationOfTownsPeople": "Mostrar pessoas da cidade no mapa",
"ShowAnimalsNeedPets": "Mostra animais q precisam de carinho",
"HideAnimalPetOnMaxFriendship": "Esconder na amizade máxima",
"ShowCropAndBarrelTooltip": "Mostrar tempo de colheita e produção",
"ShowItemEffectRanges": "Exibe alcance do espantalho/aspersor",
"ShowExtraItemInformation": "Exibir infos extras sobre os items",
"ShowHarvestPricesInShop": "Mostrar preços de colheita na loja",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Inventário: Exibir ícone calendário",
"ShowHeartFills": "Social: Exibir preench. dos corações",
"ShowTodaysGifts": "Social: Exibir presentes dados",
//Settings - Show icons
"ShowBirthdayIcon": "Exibir ícone dos aniversariantes",
"HideBirthdayIfFullFriendShip": "Esconder na amizade máxima",
"ShowTravelingMerchant": "Exibir ícone do Carrinho de Viagem",
"HideMerchantWhenVisited": "Esconder quando visitado",
"ShowRainyDay": "Exibir ícone sobre dias chuvosos",
"ShowWhenNewRecipesAreAvailable": "Exibir ícone da Rainha do Molho",
"ShowToolUpgradeStatus": "Exibir ícone de melhorias do Clint",
"ShowRobinBuildingStatusIcon": "Exibir ícone de construções da Robin",
"ShowLuckIcon": "Exibir ícone de sorte",
"ShowExactValue": "Mostra o valor exato",
"ShowSeasonalBerry": "Mostrar coletáveis sazonais",
"ShowSeasonalBerryHazelnut": "Mostrar avelãs nas árvores",
//Others
"LevelUp": "Level Up"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/ru.json
{
//GUI - Billboard
"Billboard": "Объявления",
"Calendar": "Календарь",
//GUI - Seed shop
"HarvestPrice": "Цена урожая",
//Production
"Days": "дней",
"Hours": "часов",
"Minutes": "минут",
//Harvest
"DaysToMature": "дней до созревания",
"ReadyToHarvest": "Готово к сбору урожая!",
//Display icons - General
"TodaysRecipe": "Сегодняшний рецепт: ",
"TravelingMerchantIsInTown": "Странствующий Торговец в городе!",
"RainNextDay": "Завтра будет дождь",
"ThunderstormNextDay": "Завтра будет гроза",
"SnowNextDay": "Завтра будет снег",
"IslandRainNextDay": "Завтра будет дождь",
"IslandThunderstormNextDay": "Завтра будет гроза",
"DaysUntilToolIsUpgraded": "{1} будет улучшено через {0} дней",
"ToolIsFinishedBeingUpgraded": "{0} улучшено!",
"NpcBirthday": "Сегодня день рождение {0}",
"RobinBuildingStatus": "Робин завершит строительство через {0} дн.",
// Display icons - Foragables
"CanFindSalmonberry": "Сегодня ты можешь найти морошку",
"CanFindBlackberry": "Сегодня ты можешь найти ежевику",
"CanFindHazelnut": "Сегодня ты можешь найти фундук",
//Display icons - Luck
"DailyLuckValue": "Ваша сегодняшняя удача: {0}",
"FeelingLucky": "Вы ощущаете, что сегодня вам точно повезёт!",
"LuckyButNotTooLucky": "Вы ощущаете, что сегодня вроде бы вам повезёт.",
"NotFeelingLuckyAtAll": "Сегодня вам совсем не везёт...",
"MaybeStayHome": "Может быть, вам стоит сегодня остаться дома...",
"LuckStatus1": "Чувствую себя везунчиком!",
"LuckStatus2": "Чувствую себя сегодня немного везунчиком!",
"LuckStatus3": "Этот день в ваших руках",
"LuckStatus4": "Сегодня духи абсолютно нейтральны",
"LuckStatus5": "Удача сегодня не на вашей стороне",
"LuckStatus6": "Может быть, вам стоит сегодня остаться дома...",
//Settings - General
"ShowLevelUpAnimation": "Показывать анимацию уровня",
"ShowExperienceBar": "Показывать панель опыта",
"AllowExperienceBarToFadeOut": "Разрешить панели опыта исчезать с экрана",
"ShowExperienceGain": "Показывать накопление опыта",
"ShowLocationOfTownsPeople": "Показывать горожан на карте",
"ShowAnimalsNeedPets": "Показывать, когда животных нужно погладить",
"HideAnimalPetOnMaxFriendship": "Скрывать при максимальной дружбе",
"ShowCropAndBarrelTooltip": "Показывать время у урожая и бочек",
"ShowItemEffectRanges": "Показывать радиус пугал и спринклеров",
"ShowExtraItemInformation": "Показывать дополнительную информацию по предметам",
"ShowHarvestPricesInShop": "Показывать цену урожая в магазине",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Показывать кнопки календаря и доски объявлений",
"ShowHeartFills": "Показывать заполнение сердец отношений",
"ShowTodaysGifts": "Показывать уже сегодняшние подарки",
//Settings - Show icons
"ShowBirthdayIcon": "Показывать иконку дня рождения",
"HideBirthdayIfFullFriendShip": "Скрывать при максимальной дружбе",
"ShowTravelingMerchant": "Показывать иконку Странствующего Торговца",
"HideMerchantWhenVisited": "Скрывать после посещения",
"ShowRainyDay": "Показать дождливые дни",
"ShowWhenNewRecipesAreAvailable": "Показывать, когда доступны новые рецепты",
"ShowToolUpgradeStatus": "Показывать статус улучшения инструмента",
"ShowRobinBuildingStatusIcon": "Показывать статус постройки с Робин",
"ShowLuckIcon": "Показывать иконку удачи",
"ShowExactValue": "Показывать точное значение",
"ShowSeasonalBerry": "Показывать сезонные растения",
"ShowSeasonalBerryHazelnut": "Показывать фундук на деревьях",
//Others
"LevelUp": "Уровень повышен"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/th.json
{
//GUI - Billboard
"Billboard": "กระดานปิดประกาศ",
"Calendar": "ปฏิทิน",
//GUI - Seed shop
"HarvestPrice": "ราคาผลผลิต",
//Production
"Days": "วัน",
"Hours": "ชั่วโมง",
"Minutes": "นาที",
//Harvest
"DaysToMature": "วัน จะเติบโตเต็มที่",
"ReadyToHarvest": "พร้อมเก็บเกี่ยวแล้ว!",
//Display icons - General
"TodaysRecipe": "เมนูวันนี้: ",
"TravelingMerchantIsInTown": "รถพ่อค้าเร่มาถึงเมืองแล้ว!",
"RainNextDay": "พรุ่งนี้ฝนจะตก",
"ThunderstormNextDay": "พรุ่งนี้จะมีพายุฝนฟ้าคะนอง",
"SnowNextDay": "พรุ่งนี้จะมีหิมะตก",
"IslandRainNextDay": "ที่เกาะพรุ่งนี้ฝนจะตก",
"IslandThunderstormNextDay": "ที่เกาะพรุ่งนี้จะมีพายุฝนฟ้าคะนอง",
"DaysUntilToolIsUpgraded": "{0} วัน จะอัพเกรด {1} เสร็จสิ้น",
"ToolIsFinishedBeingUpgraded": "{0} เสร็จเรียบร้อย!",
"NpcBirthday": "วันเกิดของ {0}",
"RobinBuildingStatus": "โรบินจะสร้างสิ่งปลูกสร้างใหม่ของคุณให้เสร็จใน {0} วัน",
// Display icons - Foragables
"CanFindSalmonberry": "คุณจะเจอ Salmonberry ได้ในวันนี้",
"CanFindBlackberry": "คุณจะเจอ Blackberries ได้ในวันนี้",
"CanFindHazelnut": "คุณจะเจอ Hazelnuts บนต้นไม้ได้ในวันนี้",
//Display icons - Luck
"DailyLuckValue": "โชคประจำวันของคุณคือ {0}",
"FeelingLucky": "คุณรู้สึกว่า โชคดีสุดๆ!!",
"LuckyButNotTooLucky": "รู้สึกเหมือนจะโชคดี... แต่ก็ไม่มาก",
"NotFeelingLuckyAtAll": "คุณรู้สึกว่า วันนี้โชคไม่เข้าข้างเอาซะเลย...",
"MaybeStayHome": "วันนี้คุณควรอยู่บ้านจะดีกว่านะ...",
"LuckStatus1": "รู้สึกโชคดี!",
"LuckStatus2": "วันนี้รู้สึกโชคดีนิดหน่อย!",
"LuckStatus3": "วันนี้อยู่ในกำมือของคุณแล้ว",
"LuckStatus4": "วันนี้เหล่าวิญญาณรู้สึกวางตัวเป็นกลาง",
"LuckStatus5": "วันนี้โชคไม่เข้าข้างคุณ",
"LuckStatus6": "บางทีวันนี้คุณควรจะอยู่บ้าน...",
//Settings - General
"ShowLevelUpAnimation": "แสดงอะนิเมชั่นการเลื่อนขั้น",
"ShowExperienceBar": "แสดงแถบค่าประสบการณ์",
"AllowExperienceBarToFadeOut": "อนุญาตให้แถบค่าประสบการณ์ จางหายไป",
"ShowExperienceGain": "แสดงค่าประสบการณ์ที่ได้รับ",
"ShowLocationOfTownsPeople": "แสดงชาวเมืองในแผนที่",
"ShowAnimalsNeedPets": "แสดงสัตว์เลี้ยงที่ต้องดูแล",
"HideAnimalPetOnMaxFriendship": "ซ่อนค่าหัวใจเมื่อมีค่าสูงสุด",
"ShowCropAndBarrelTooltip": "แสดงเวลาของผักและถังบ่ม",
"ShowItemEffectRanges": "แสดงระยะของหุ่นไล่กาและสปริงเกอร์",
"ShowExtraItemInformation":
"แสดงข้อมูลไอเท็มเมื่อชี้เมาส์",
"ShowHarvestPricesInShop": "แสดงราคาขายผลผลิตในร้านค้า",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "แสดงปุ่มปฏิทิน/กระดานปิดประกาศ",
"ShowHeartFills": "แสดงค่าหัวใจ",
"ShowTodaysGifts": "แสดงของขวัญที่ให้ในวันนี้",
//Settings - Show icons
"ShowBirthdayIcon": "แสดงสัญลักษณ์ วันเกิด",
"HideBirthdayIfFullFriendShip": "ซ่อนค่าหัวใจเมื่อมีค่าสูงสุด",
"ShowTravelingMerchant": "แสดงรถพ่อค้าเร่",
"HideMerchantWhenVisited": "ซ่อนรถพ่อค้าเร่ หากไปมาแล้ว",
"ShowRainyDay": "แสดงวันที่ฝนตก",
"ShowWhenNewRecipesAreAvailable": "แสดงเมื่อมีสูตรอาหาร/สูตรคราฟชนิดใหม่เกิดขึ้น",
"ShowToolUpgradeStatus": "แสดงสถานะการอัพเกรดอุปกรณ์",
"ShowRobinBuildingStatusIcon": "แสดงสถานะสิ่งปลูกสร้างของโรบิน",
"ShowLuckIcon": "แสดงสัญลักษณ์โชค",
"ShowExactValue": "แสดงมูลค่าจริง",
"ShowSeasonalBerry": "แสดงของป่าตามฤดูกาล",
"ShowSeasonalBerryHazelnut": "แสดง Hazelnuts บนต้นไม้",
//Others
"LevelUp": "เลื่อนขั้น"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/tr.json
{
//GUI - Billboard
"Billboard": "İlan panosu",
"Calendar": "Takvim",
//GUI - Seed shop
"HarvestPrice": "Hasat ücreti",
//Production
"Days": "gün",
"Hours": "saat",
"Minutes": "dakika",
//Harvest
"DaysToMature": "olgunlaşmasına kalan zaman",
"ReadyToHarvest": "Hasata hazır!",
//Display icons - General
"TodaysRecipe": "Bugün'ün tarifi: ",
"TravelingMerchantIsInTown": "Gezgin tüccar kasabada!",
"RainNextDay": "Yarın yamur yağacak",
"ThunderstormNextDay": "Yarın fırtına olacak",
"SnowNextDay": "Yarın kar yağacak",
"IslandRainNextDay": "Yarın adada yamur yağacak",
"IslandThunderstormNextDay": "Yarın adada fırtına olacak",
"DaysUntilToolIsUpgraded": "{1}'nın yükseltmesinin bitmesine {0} gün kaldı ",
"ToolIsFinishedBeingUpgraded": "{0} hazır!",
"NpcBirthday": "Doğum günü: {0}",
"RobinBuildingStatus": "Robin inşaatı {0} günde bitirecek",
// Display icons - Foragables
"CanFindSalmonberry": "Bugün Ağaç Çileği bulabilirsin",
"CanFindBlackberry": "Bugün Yabani Böğürtlen bulabilirsin",
"CanFindHazelnut": "Bugün ağaçlarda Fındık bulabilirsin",
//Display icons - Luck
"DailyLuckValue": "Bugünkü şans değerin {0}",
"FeelingLucky": "Şanslı hissediyorsun!!",
"LuckyButNotTooLucky": "Şanslı hissediyorsun... ama çok da değil",
"NotFeelingLuckyAtAll": "Bugün pek de şanslı hissetmiyorsun...",
"MaybeStayHome": "Belki de bugün evde kalmalısın...",
"LuckStatus1": "Şanslı hissediyorsun!",
"LuckStatus2": "Bugün biraz şanslı hissediyorsun!",
"LuckStatus3": "Şansın bugün kendi ellerinde",
"LuckStatus4": "Ruhlar bugün tarafsız hissediyor",
"LuckStatus5": "Şans bugün yanında değil",
"LuckStatus6": "Belki de bugün evde kalmalısın...",
//Settings - General
"ShowLevelUpAnimation": "Seviye atlama animasyonunu göster",
"ShowExperienceBar": "Deneyim çubuğu görünebilsin",
"AllowExperienceBarToFadeOut": "Deneyim çubuğu kaybolabilsin",
"ShowExperienceGain": "Deneyim kazanıldığını göster",
"ShowLocationOfTownsPeople": "Kasaba halkını haritada göster",
"ShowAnimalsNeedPets": "Sevilme ihtiyacı olan hayvanları göster",
"HideAnimalPetOnMaxFriendship": "Maksimumda gizle",
"ShowCropAndBarrelTooltip": "Ekin ve varil sürelerini göster",
"ShowItemEffectRanges": "Korkuluk ve fıskiye menzilini göster",
"ShowExtraItemInformation": "Nesneler hakkında ek bilgileri göster",
"ShowHarvestPricesInShop": "Dükkanda ürün satış fiyatını göster",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Takvim/ilan pano düğmesini göster",
"ShowHeartFills": "Kalbin ne kadarının dolduğunu göster",
"ShowTodaysGifts": "Bugün verilen hediyeleri göster",
//Settings - Show icons
"ShowBirthdayIcon": "Doğum günü ikonunu göster",
"HideBirthdayIfFullFriendShip": "Maksimumda gizle",
"ShowTravelingMerchant": "Gezgin tüccar ikonunu göster",
"HideMerchantWhenVisited": "Ziyaret edince gizle",
"ShowRainyDay": "Yağmurlu günleri göster",
"ShowWhenNewRecipesAreAvailable": "Yeni tarifler mevcut olduğunda göster",
"ShowToolUpgradeStatus": "Alet yükseltme durumunu göster",
"ShowRobinBuildingStatusIcon": "Robin'in inşaa durumunu göster",
"ShowLuckIcon": "Şans ikonunu göster",
"ShowExactValue": "Tam değeri göster",
"ShowSeasonalBerry": "Mevsimsel yemişleri göster",
"ShowSeasonalBerryHazelnut": "Ağaçlardaki fındıkları göster",
//Others
"LevelUp": "Seviye atlama"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/uk.json
{
//GUI - Billboard
"Billboard": "Оголошення",
"Calendar": "Календар",
//GUI - Seed shop
"HarvestPrice": "Ціна врожаю",
//Production
"Days": "дні(в)",
"Hours": "годин",
"Minutes": "хвилин",
//Harvest
"DaysToMature": "дні(в) для дозрівання",
"ReadyToHarvest": "Готово до збору!",
//Display icons - General
"TodaysRecipe": "Сьогоднішній рецепт: ",
"TravelingMerchantIsInTown": "Мандрівний купець у місті!",
"RainNextDay": "Завтра буде дощ",
"ThunderstormNextDay": "Завтра буде гроза",
"SnowNextDay": "Завтра буде сніг",
"IslandRainNextDay": "Завтра буде дощ",
"IslandThunderstormNextDay": "Завтра буде гроза",
"DaysUntilToolIsUpgraded": "Залишилося {0} дні(в), щоб {1} було оновлено",
"ToolIsFinishedBeingUpgraded": "{0} оновлено!",
"NpcBirthday": "{0} - день народження",
"RobinBuildingStatus": "Робін завершить нову будівлю за {0} дні(в)",
// Display icons - Foragables
"CanFindSalmonberry": "Сьогодні ви можете знайти кущі малини",
"CanFindBlackberry": "Сьогодні ви можете знайти кущі ожини",
"CanFindHazelnut": "Cьогодні ви можете знайти фундук на деревах",
//Display icons - Luck
"DailyLuckValue": "Ваша щоденна удача - {0}",
"FeelingLucky": "Вам сьогодні пощастить!!",
"LuckyButNotTooLucky": "Вам сьогодні трохи пощастить",
"NotFeelingLuckyAtAll": "Вам сьогодні не щаститиме цілий день...",
"MaybeStayHome": "Можливо, сьогодні вам слід залишися вдома...",
"LuckStatus1": "Вам сьогодні пощастить!",
"LuckStatus2": "Вам сьогодні трохи пощастить!",
"LuckStatus3": "День у ваших руках",
"LuckStatus4": "Духи сьогодні абсолютно нейтральні.",
"LuckStatus5": "Сьогодні удача не буде на вашому боці.",
"LuckStatus6": "Можливо, сьогодні вам слід залишися вдома...",
//Settings - General
"ShowLevelUpAnimation": "Показ. анім. переходу до нового рівня",
"ShowExperienceBar": "Показувати шкалу досвіду",
"AllowExperienceBarToFadeOut": "Дозволити шкалі досвіду зникати",
"ShowExperienceGain": "Показати набутий досвід",
"ShowLocationOfTownsPeople": "Показувати містян на мапі",
"ShowAnimalsNeedPets": "Показ., що тварин треба попестити",
"HideAnimalPetOnMaxFriendship": "Схов. за макс. рівня дружби",
"ShowCropAndBarrelTooltip": "Показ. час вир. культур, вар. в бочках",
"ShowItemEffectRanges": "Показ. радіус дії опудал і спринклерів",
"ShowExtraItemInformation": "Показ. додаткове інфо про предмет",
"ShowHarvestPricesInShop": "Показувати ціни врожаю у крамниці",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "Показ. кноп. календаря/дошки огол.",
"ShowHeartFills": "Показ. запов. сердець стосунків",
"ShowTodaysGifts": "Показ. сьогод. даровані подарунки",
//Settings - Show icons
"ShowBirthdayIcon": "Показувати іконку дня народження",
"HideBirthdayIfFullFriendShip": "Схов. за макс. рівня дружби",
"ShowTravelingMerchant": "Показувати іконку мандрівного купця",
"HideMerchantWhenVisited": "Сховати після відвідування",
"ShowRainyDay": "Показувати дощові дні",
"ShowWhenNewRecipesAreAvailable": "Показ., коли доступні нові рецепти",
"ShowToolUpgradeStatus": "Показ. статус онов. інструмента",
"ShowRobinBuildingStatusIcon": "Показ. статус роб. Робін над побуд.",
"ShowLuckIcon": "Показувати іконку удачі",
"ShowExactValue": "Показувати точне значення",
"ShowSeasonalBerry": "Показувати сезонні лісові ягоди",
"ShowSeasonalBerryHazelnut": "Показ., чи є фундук на деревах",
//Others
"LevelUp": "Новий рівень"
}
UIInfoSuite2-master/UIInfoSuite2/i18n/zh.json
{
//GUI - Billboard
"Billboard": "布告栏",
"Calendar": "日历",
//GUI - Seed shop
"HarvestPrice":
"收获价",
//Production
"Days": "天",
"Hours": "小时",
"Minutes": "分钟",
//Harvest
"DaysToMature": "天后成熟",
"ReadyToHarvest": "可以收获!",
//Display icons - General
"TodaysRecipe": "今日食谱:",
"TravelingMerchantIsInTown": "旅行商人在镇上!",
"RainNextDay": "明日有雨",
"ThunderstormNextDay": "明日雷雨",
"SnowNextDay": "明日有雪",
"IslandRainNextDay": "明日有雨",
"IslandThunderstormNextDay": "明日雷雨",
"DaysUntilToolIsUpgraded": "{1}还有{0}天完成!",
"ToolIsFinishedBeingUpgraded": "{0}完成了!",
"NpcBirthday": "{0}的生日",
"RobinBuildingStatus": "罗宾将在{0}天内完成您的新建筑",
// Display icons - Foragables
"CanFindSalmonberry": "今天可以找到美洲大树莓",
"CanFindBlackberry": "今天可以找到黑莓",
"CanFindHazelnut": "今天可以在树上找到榛子",
//Display icons - Luck
"DailyLuckValue": "你今日的幸运值为{0}",
"FeelingLucky": "你感到今天很幸运!!",
"LuckyButNotTooLucky": "你感到很幸运……但不是太幸运",
"NotFeelingLuckyAtAll": "你今天一点都不幸运……",
"MaybeStayHome": "也许你今天应该呆在家里……",
"LuckStatus1": "你感到今天很幸运",
"LuckStatus2": "你感到今天有点幸运",
"LuckStatus3": "今天掌握在你的手中",
"LuckStatus4": "今天精灵们十分冷漠",
"LuckStatus5": "今天运气不会站在你这边",
"LuckStatus6": "也许你今天应该呆在家里……",
//Settings - General
"ShowLevelUpAnimation": "显示升级动画",
"ShowExperienceBar": "显示经验条",
"AllowExperienceBarToFadeOut": "允许经验条淡出",
"ShowExperienceGain": "显示获得的经验值",
"ShowLocationOfTownsPeople": "在地图上显示NPC",
"ShowAnimalsNeedPets": "显示动物需要关爱的图标",
"HideAnimalPetOnMaxFriendship": "在动物达到最大心情时隐藏",
"ShowCropAndBarrelTooltip": "显示作物与桶的剩余时间",
"ShowItemEffectRanges": "显示稻草人与洒水器的范围",
"ShowExtraItemInformation": "鼠标悬停时显示物品信息",
"ShowHarvestPricesInShop": "在购买页面显示收获价格",
//Settings - Tabs info
"DisplayCalendarAndBillboard": "显示日历与布告栏按钮",
"ShowHeartFills": "显示关系的爱心填充",
"ShowTodaysGifts": "显示今天赠送的礼物",
//Settings - Show icons
"ShowBirthdayIcon": "显示NPC生日图标",
"HideBirthdayIfFullFriendShip": "满好感度时隐藏生日图标",
"ShowTravelingMerchant": "显示旅行商人",
"HideMerchantWhenVisited": "在访问后隐藏",
"ShowRainyDay": "显示雨天",
"ShowWhenNewRecipesAreAvailable": "显示可学习的新食谱",
"ShowToolUpgradeStatus": "显示工具升级状态",
"ShowRobinBuildingStatusIcon": "显示罗宾工作进度",
"ShowLuckIcon": "显示运气图标",
"ShowExactValue": "显示精确的值",
"ShowSeasonalBerry": "显示季节采摘物",
"ShowSeasonalBerryHazelnut": "显示树上的榛子",
//Others
"LevelUp": "升级"
}
UIInfoSuite2-master/UIInfoSuite2/manifest.json
{
"Name": "UI Info Suite 2",
"Author": "Annosz",
"Version": "2.2.9",
"Description": "Adds a useful information to the user interface. Based on Cdaragorn's excellent UI Info Suite.",
"UniqueID": "Annosz.UiInfoSuite2",
"EntryDll": "UIInfoSuite2.dll",
"MinimumApiVersion": "3.0.0",
"UpdateKeys": [ "GitHub:Annosz/UIInfoSuite2" ]
}
UIInfoSuite2-master/docs/shipping_bin.md
### Shipping bin notes
[svw:Shipping]: https://stardewvalleywiki.com/Shipping
The "Full Shipment" achievement is awarded when the player has [shipped all items](svw:Shipping) in the "Items shipped (Farm & Forage)" tab of the Collections page (`CollectionsPage`) in the game menu.
The game tries to give the achievement to the player when it displays the shipping summary menu (`ShippingMenu`) during the night. It uses `Utility.hasFarmerShippedAllItems` to check if the requirements are met. This means that the "Full Shipment" achievement can only be earned on nights during which the player has shipped some items.
The "Full Shipment" achievement is achievement number 34. An item is shipped if it is present in thes `Game1.player.basicShipped` dictionary.
There are 4 points at which the shipping items are determined:
- In `Utility.getFarmerItemsShippedPercent` used by `ShippingMenu` via `Utility.hasFarmerShippedAllItems` to award the achievement
- In the constructor of `CollectionsPage` to show the basic shipped items tab of collections page of the game menu
- In `Utility.highlightShippableObjects` used by `ShippingBin.doAction` and `IslandWest.checkAction` to who shippable items when interacting with the shipping bin
- In `Object.countsForShippedCollection` used by `ShippingMenu` via `ShippingMenu.parseItems` to select which items we track the shipping count
**Utility.getFarmerItemsShippedPercent** goes through _Game1.objectInformation_ and keeps items that:
- Are not artifacts: `!text.Contains("Arch")`
- Are not fishing items: `!text.Contains("Fish")`
- Are not minerals: `!text.Contains("Mineral")`
- Are not gems (category): `!text.Substring(text.Length - 3).Equals("-2")` [note 1]
- Are not cooking items: `!text.Contains("Cooking")`
- Are not cooking (category): `!text.Substring(text.Length - 3).Equals("-7")` [note 1]
- And are basicShipped items (see below): `Object.isPotentialBasicShippedCategory(...)`
Where `text` is the 4th field of `ObjectInformation.json` representing the item type and category.
\[note 1]: That code seems to always return true if I'm not mistaken?
**CollectionsPage** goes through a sorted _Game1.objectInformation_ and keeps items that:
- Are not artifacts: `if (text.Contains("Arch"))`
- Are not fishing items: `else if (text.Contains("Fish"))`
- Are not minerals or gems (category): `else if (text.Contains("Mineral") || text.Substring(text.Length - 3).Equals("-2"))` [note 2]
- Are not cooking items or cooking (category): `else if (text.Contains("Cooking") || text.Substring(text.Length - 3).Equals("-7"))` [note 2]
- And are basicShipped items (see below): `if (!Object.isPotentialBasicShippedCategory(...))`
Where `text` is the 4th field of `ObjectInformation.json` representing the item type and category.
\[note 2]: The `text.Substring(...).Equals(...)` code seems to always return false if I'm not mistaken?
**Utility.highlightShippableObjects** calls `Object.canBeShipped` which keeps items that:
- Are objects: `if (i is Object)`
- Are not big craftables: `!this.bigCraftable`
- Have a type, which (usually) excludes items that aren't normal objects or big craftables: `this.type != null`
- Are not quest items: `!this.type.Equals("Quest")`
- Can be trashed: `this.canBeTrashed()`
- Are not furniture: `!(this is Furniture)`
- Are not wallpapers: `!(this is Wallpaper)`
Where `this` is an `StardewValley.Object`.
**Object.countsForShippedCollection** checks the object's information and keeps items that:
- Have a type, which (usually) excludes items that aren't normal objects or big craftables: `if (this.type == null ...)`
- Are not artifacts: `if (... this.type.Contains("Arch") ...)`
- Are not big craftables: `if (... (bool)this.bigCraftable)`
- Have item id 433 (Coffee Beam) or...
- Have a category: `case 0:`
- Are not in the following categories:
- Seeds: `case -74:`
- Equipment: `case -29:`
- Furniture: `case -24:`
- Tackle: `case -22:`
- Bait: `case -21:`
- Junk: `case -20:`
- Fertilizer: `case -19:`
- Meat: `case -14:`
- Minerals: `case -12:`
- Crafting: `case -8:`
- Cooking: `case -7:`
- Gem: `case -2:`
- Have an item index that is basicShipped (see below): `Object.isIndexOkForBasicShippedCategory`
**Object.isPotentialBasicShippedCategory** checks the object's category and keeps items that:
- Have item id 433 (Coffee Bean) or...
- Have a category `case 0:`
- Are not in the following categories:
- Seeds: `case -74:`
- Equipment: `case -29:`
- Furniture: `case -24:`
- Tackle: `case -22:`
- Bait: `case -21:`
- Junk: `case -20:`
- Fertilizer: `case -19:`
- Meat: `case -14:`
- Minerals: `case -12:`
- Crafting: `case -8:`
- Cooking: `case -7:`
- Gem: `case -2:`
- Have an item index that is basicShipped (see below): `Object.isIndexOkForBasicShippedCategory`
**Object.isIndexOkForBasicShippedCategory** checks that the object id:
- Is not 434 (Stardrop)
- Is not 889 (Qi Fruit)
- Is not 928 (Golden Egg)
Other functions of note:
- **ShippingBin.shipItem** and **Farm.shipItem** adds the item to the shipping bin
- **Game1.player.shippedBasic** keeps track of shipped items statistic