Refactored getDynamicTilesInfo from TileInfo.cs by moving most of it's functionality to new file DynamicTiles.cs.
Refactored handling of dynamic tiles for events; tile coords now loaded from json file. Moved LoadJsonFile function to utils.cs.master
parent
35115223d3
commit
8509cbfc0b
|
@ -65,7 +65,7 @@ namespace stardew_access.ScreenReader
|
||||||
/// <returns>Name of the object. Returns null if no object found.</returns>
|
/// <returns>Name of the object. Returns null if no object found.</returns>
|
||||||
public string? GetNameAtTile(Vector2 tile)
|
public string? GetNameAtTile(Vector2 tile)
|
||||||
{
|
{
|
||||||
return TileInfo.getNameAtTile(tile, null);
|
return TileInfo.GetNameAtTile(tile, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speaks the text via the loaded screen reader (if any).</summary>
|
/// <summary>Speaks the text via the loaded screen reader (if any).</summary>
|
||||||
|
|
|
@ -0,0 +1,782 @@
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Netcode;
|
||||||
|
using StardewValley;
|
||||||
|
using StardewValley.Buildings;
|
||||||
|
using StardewValley.Locations;
|
||||||
|
using StardewValley.Objects;
|
||||||
|
using StardewValley.TerrainFeatures;
|
||||||
|
using static stardew_access.Features.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace stardew_access.Features
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides methods to locate tiles of interest in various game locations that are conditional or unpredictable (I.E. not static).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The DynamicTiles class currently supports the following location types:
|
||||||
|
/// - Beach
|
||||||
|
/// - BoatTunnel
|
||||||
|
/// - CommunityCenter
|
||||||
|
/// - Farm
|
||||||
|
/// - FarmHouse
|
||||||
|
/// - Forest
|
||||||
|
/// - IslandFarmHouse
|
||||||
|
/// - IslandLocation
|
||||||
|
/// - LibraryMuseum
|
||||||
|
/// - Town
|
||||||
|
///
|
||||||
|
/// And the following Island LocationTypes:
|
||||||
|
/// - IslandNorth
|
||||||
|
/// - IslandWest
|
||||||
|
/// - VolcanoDungeon
|
||||||
|
///
|
||||||
|
/// The class also supports the following named locations:
|
||||||
|
/// - Barn (and its upgraded versions)
|
||||||
|
/// - Coop (and its upgraded versions)
|
||||||
|
///
|
||||||
|
/// The class does not yet support the following location types, but consider adding support in future updates:
|
||||||
|
/// - AbandonedJojaMart
|
||||||
|
/// - AdventureGuild
|
||||||
|
/// - BathHousePool
|
||||||
|
/// - BeachNightMarket
|
||||||
|
/// - BugLand
|
||||||
|
/// - BusStop
|
||||||
|
/// - Caldera
|
||||||
|
/// - Cellar
|
||||||
|
/// - Club
|
||||||
|
/// - Desert
|
||||||
|
/// - FarmCave
|
||||||
|
/// - FishShop
|
||||||
|
/// - JojaMart
|
||||||
|
/// - ManorHouse
|
||||||
|
/// - MermaidHouse
|
||||||
|
/// - Mine
|
||||||
|
/// - Mountain
|
||||||
|
/// - MovieTheater
|
||||||
|
/// - Railroad
|
||||||
|
/// - SeedShop
|
||||||
|
/// - Sewer
|
||||||
|
/// - Submarine
|
||||||
|
/// - Summit
|
||||||
|
/// - WizardHouse
|
||||||
|
/// - Woods
|
||||||
|
///
|
||||||
|
/// The class does not yet support the following named locations, but consider adding support in future updates:
|
||||||
|
/// - "AnimalShop"
|
||||||
|
/// - "Backwoods"
|
||||||
|
/// - "BathHouse_Entry"
|
||||||
|
/// - "BathHouse_MensLocker"
|
||||||
|
/// - "BathHouse_WomensLocker"
|
||||||
|
/// - "Blacksmith"
|
||||||
|
/// - "ElliottHouse"
|
||||||
|
/// - "FarmGreenHouse"
|
||||||
|
/// - "Greenhouse"
|
||||||
|
/// - "HaleyHouse"
|
||||||
|
/// - "HarveyRoom"
|
||||||
|
/// - "Hospital"
|
||||||
|
/// - "JoshHouse"
|
||||||
|
/// - "LeahHouse"
|
||||||
|
/// - "LeoTreeHouse"
|
||||||
|
/// - "Saloon"
|
||||||
|
/// - "SamHouse"
|
||||||
|
/// - "SandyHouse"
|
||||||
|
/// - "ScienceHouse"
|
||||||
|
/// - "SebastianRoom"
|
||||||
|
/// - "SkullCave"
|
||||||
|
/// - "Sunroom"
|
||||||
|
/// - "Tent"
|
||||||
|
/// - "Trailer"
|
||||||
|
/// - "Trailer_Big"
|
||||||
|
/// - "Tunnel"
|
||||||
|
/// - "WitchHut"
|
||||||
|
/// - "WitchSwamp"
|
||||||
|
/// - "WitchWarpCave"
|
||||||
|
/// - "WizardHouseBasement"
|
||||||
|
///
|
||||||
|
/// The class does not yet support the following IslandLocation location types, but consider adding support in future updates:
|
||||||
|
/// - IslandEast
|
||||||
|
/// - IslandFarmCave
|
||||||
|
/// - IslandFieldOffice
|
||||||
|
/// - IslandHut
|
||||||
|
/// - IslandShrine
|
||||||
|
/// - IslandSouth
|
||||||
|
/// - IslandSouthEast
|
||||||
|
/// - IslandSouthEastCave
|
||||||
|
/// - IslandWestCave1
|
||||||
|
///
|
||||||
|
/// The class does not yet support the following IslandLocation named locations, but consider adding support in future updates:
|
||||||
|
/// - "CaptainRoom"
|
||||||
|
/// - "IslandNorthCave1"
|
||||||
|
/// - "QiNutRoom"
|
||||||
|
/// </remarks>
|
||||||
|
public class DynamicTiles
|
||||||
|
{
|
||||||
|
// Static instance for the singleton pattern
|
||||||
|
private static DynamicTiles? _instance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The singleton instance of the <see cref="DynamicTiles"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public static DynamicTiles Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_instance ??= new DynamicTiles();
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashSet for storing which unimplemented locations have been previously logged
|
||||||
|
private static readonly HashSet<object> loggedLocations = new();
|
||||||
|
|
||||||
|
// Dictionary of coordinates for feeding benches in barns and coops
|
||||||
|
private static readonly Dictionary<string, (int minX, int maxX, int y)> FeedingBenchBounds = new()
|
||||||
|
{
|
||||||
|
{ "Barn", (8, 11, 3) },
|
||||||
|
{ "Barn2", (8, 15, 3) },
|
||||||
|
{ "Big Barn", (8, 15, 3) },
|
||||||
|
{ "Barn3", (8, 19, 3) },
|
||||||
|
{ "Deluxe Barn", (8, 19, 3) },
|
||||||
|
{ "Coop", (6, 9, 3) },
|
||||||
|
{ "Coop2", (6, 13, 3) },
|
||||||
|
{ "Big Coop", (6, 13, 3) },
|
||||||
|
{ "Coop3", (6, 17, 3) },
|
||||||
|
{ "Deluxe Coop", (6, 17, 3) }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dictionary to hold event info
|
||||||
|
private static readonly Dictionary<string, Dictionary<(int X, int Y), string>> EventInteractables;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicTiles"/> class.
|
||||||
|
/// Loads the event file.
|
||||||
|
/// </summary>
|
||||||
|
static DynamicTiles()
|
||||||
|
{
|
||||||
|
EventInteractables = LoadEventTiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads event tiles from the "event-tiles.json" file and returns a dictionary representation of the data.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A dictionary with event names as keys and nested dictionaries as values, where nested dictionaries have
|
||||||
|
/// coordinate tuples (x, y) as keys and tile names as values.
|
||||||
|
/// </returns>
|
||||||
|
private static Dictionary<string, Dictionary<(int x, int y), string>> LoadEventTiles()
|
||||||
|
{
|
||||||
|
JsonElement json = LoadJsonFile("event-tiles.json");
|
||||||
|
|
||||||
|
if (json.ValueKind == JsonValueKind.Undefined)
|
||||||
|
{
|
||||||
|
// If the JSON couldn't be loaded or parsed, return an empty dictionary
|
||||||
|
return new Dictionary<string, Dictionary<(int x, int y), string>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventTiles = new Dictionary<string, Dictionary<(int x, int y), string>>();
|
||||||
|
|
||||||
|
// Iterate over the JSON properties to create a dictionary representation of the data
|
||||||
|
foreach (JsonProperty eventProperty in json.EnumerateObject())
|
||||||
|
{
|
||||||
|
string eventName = eventProperty.Name;
|
||||||
|
var coordinates = new Dictionary<(int x, int y), string>();
|
||||||
|
|
||||||
|
// Iterate over the coordinate properties to create a nested dictionary with coordinate tuples as keys
|
||||||
|
foreach (JsonProperty coordinateProperty in eventProperty.Value.EnumerateObject())
|
||||||
|
{
|
||||||
|
string[] xy = coordinateProperty.Name.Split(',');
|
||||||
|
int x = int.Parse(xy[0]);
|
||||||
|
int y = int.Parse(xy[1]);
|
||||||
|
coordinates.Add((x, y), value: coordinateProperty.Value.GetString() ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventTiles.Add(eventName, coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventTiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in a Beach.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beach">The Beach to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetBeachInfo(Beach beach, int x, int y)
|
||||||
|
{
|
||||||
|
if (MainClass.ModHelper == null)
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
if (MainClass.ModHelper.Reflection.GetField<NPC>(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y))
|
||||||
|
{
|
||||||
|
return ("Old Mariner", CATEGORY.NPCs);
|
||||||
|
}
|
||||||
|
else if (x == 58 && y == 13)
|
||||||
|
{
|
||||||
|
if (!beach.bridgeFixed.Value)
|
||||||
|
{
|
||||||
|
return ("Repair Bridge", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ("Bridge", CATEGORY.Bridges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a BoatTunnel.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="boatTunnel">The BoatTunnel to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetBoatTunnelInfo(BoatTunnel boatTunnel, int x, int y)
|
||||||
|
{
|
||||||
|
if (boatTunnel is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(boatTunnel));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x == 4 && y == 9)
|
||||||
|
{
|
||||||
|
return ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatFixed") ? "Repair " : "") + "Ticket Machine", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (x == 6 && y == 8)
|
||||||
|
{
|
||||||
|
return ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull") ? "Repair " : "") + "Boat Hull", (!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull") ? CATEGORY.Interactables : CATEGORY.Decor));
|
||||||
|
}
|
||||||
|
else if (x == 8 && y == 9)
|
||||||
|
{
|
||||||
|
return ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor") ? "Repair " : "") + "Boat Anchor", (!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor") ? CATEGORY.Interactables : CATEGORY.Decor));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a CommunityCenter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="communityCenter">The CommunityCenter to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetCommunityCenterInfo(CommunityCenter communityCenter, int x, int y)
|
||||||
|
{
|
||||||
|
if (communityCenter.missedRewardsChestVisible.Value && x == 22 && y == 10)
|
||||||
|
{
|
||||||
|
return ("Missed Rewards Chest", CATEGORY.Containers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the building information for a given position on a farm.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="building">The Building instance.</param>
|
||||||
|
/// <param name="x">The x-coordinate of the position.</param>
|
||||||
|
/// <param name="y">The y-coordinate of the position.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the door or building found, or (null, null) if no door or building is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetBuildingInfo(Building building, int x, int y)
|
||||||
|
{
|
||||||
|
string name = building.buildingType.Value;
|
||||||
|
int buildingTileX = building.tileX.Value;
|
||||||
|
int buildingTileY = building.tileY.Value;
|
||||||
|
|
||||||
|
// If the building is a FishPond, prepend the fish name
|
||||||
|
if (building is FishPond fishPond && fishPond.fishType.Value >= 0)
|
||||||
|
{
|
||||||
|
name = $"{Game1.objectInformation[fishPond.fishType.Value].Split('/')[4]} {name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate differences in x and y coordinates
|
||||||
|
int offsetX = x - buildingTileX;
|
||||||
|
int offsetY = y - buildingTileY;
|
||||||
|
|
||||||
|
// Check if the position matches the human door
|
||||||
|
if (building.humanDoor.Value.X == offsetX && building.humanDoor.Value.Y == offsetY)
|
||||||
|
{
|
||||||
|
return (name + " Door", CATEGORY.Doors);
|
||||||
|
}
|
||||||
|
// Check if the position matches the animal door
|
||||||
|
else if (building.animalDoor.Value.X == offsetX && building.animalDoor.Value.Y == offsetY)
|
||||||
|
{
|
||||||
|
return (name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed"), CATEGORY.Doors);
|
||||||
|
}
|
||||||
|
// Check if the position matches the building's top-left corner
|
||||||
|
else if (offsetX == 0 && offsetY == 0)
|
||||||
|
{
|
||||||
|
return (name, CATEGORY.Buildings);
|
||||||
|
}
|
||||||
|
// Special handling for Mill buildings
|
||||||
|
else if (building is Mill)
|
||||||
|
{
|
||||||
|
// Check if the position matches the input
|
||||||
|
if (offsetX == 1 && offsetY == 1)
|
||||||
|
{
|
||||||
|
return (name + " input", CATEGORY.Buildings);
|
||||||
|
}
|
||||||
|
// Check if the position matches the output
|
||||||
|
else if (offsetX == 3 && offsetY == 1)
|
||||||
|
{
|
||||||
|
return (name + " output", CATEGORY.Buildings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the building name for any other position within the building's area
|
||||||
|
return (name, CATEGORY.Buildings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a Farm.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="farm">The Farm to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetFarmInfo(Farm farm, int x, int y)
|
||||||
|
{
|
||||||
|
var mainMailboxPos = farm.GetMainMailboxPosition();
|
||||||
|
Building building = farm.getBuildingAt(new Vector2(x, y));
|
||||||
|
|
||||||
|
if (mainMailboxPos.X == x && mainMailboxPos.Y == y)
|
||||||
|
{
|
||||||
|
return ("Mail box", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (building is not null) // Check if there is a building at the current position
|
||||||
|
{
|
||||||
|
return GetBuildingInfo(building, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a FarmHouse.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="farmHouse">The FarmHouse to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetFarmHouseInfo(FarmHouse farmHouse, int x, int y)
|
||||||
|
{
|
||||||
|
if (farmHouse.upgradeLevel >= 1)
|
||||||
|
{
|
||||||
|
int kitchenX = farmHouse.getKitchenStandingSpot().X;
|
||||||
|
int kitchenY = farmHouse.getKitchenStandingSpot().Y - 1;
|
||||||
|
|
||||||
|
if (kitchenX == x && kitchenY == y)
|
||||||
|
{
|
||||||
|
return ("Stove", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (kitchenX + 1 == x && kitchenY == y)
|
||||||
|
{
|
||||||
|
return ("Sink", CATEGORY.Others);
|
||||||
|
}
|
||||||
|
else if (farmHouse.fridgePosition.X == x && farmHouse.fridgePosition.Y == y)
|
||||||
|
{
|
||||||
|
return ("Fridge", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a Forest.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="forest">The Forest to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetForestInfo(Forest forest, int x, int y)
|
||||||
|
{
|
||||||
|
if (forest.travelingMerchantDay && x == 27 && y == 11)
|
||||||
|
{
|
||||||
|
return ("Travelling Cart", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (forest.log != null && x == 2 && y == 7)
|
||||||
|
{
|
||||||
|
return ("Log", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (forest.log == null && x == 0 && y == 7)
|
||||||
|
{
|
||||||
|
return ("Secret Woods Entrance", CATEGORY.Doors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in an IslandFarmHouse.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="islandFarmHouse">The IslandFarmHouse to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetIslandFarmHouseInfo(IslandFarmHouse islandFarmHouse, int x, int y)
|
||||||
|
{
|
||||||
|
int fridgeX = islandFarmHouse.fridgePosition.X;
|
||||||
|
int fridgeY = islandFarmHouse.fridgePosition.Y;
|
||||||
|
if (fridgeX - 2 == x && fridgeY == y)
|
||||||
|
{
|
||||||
|
return ("Stove", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (fridgeX - 1 == x && fridgeY == y)
|
||||||
|
{
|
||||||
|
return ("Sink", CATEGORY.Others);
|
||||||
|
}
|
||||||
|
else if (fridgeX == x && fridgeY == y)
|
||||||
|
{
|
||||||
|
return ("Fridge", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in an IslandNorth.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="islandNorth">The IslandNorth to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetIslandNorthInfo(IslandNorth islandNorth, int x, int y)
|
||||||
|
{
|
||||||
|
// Check if the trader is activated and the coordinates match the trader's location
|
||||||
|
if (islandNorth.traderActivated.Value && x == 36 && y == 71)
|
||||||
|
{
|
||||||
|
return ("Island Trader", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return (null, null) if no relevant object is found
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in an IslandWest.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="islandWest">The IslandWest to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetIslandWestInfo(IslandWest islandWest, int x, int y)
|
||||||
|
{
|
||||||
|
// Check if the coordinates match the shipping bin's location
|
||||||
|
if ((islandWest.shippingBinPosition.X == x || (islandWest.shippingBinPosition.X + 1) == x) && islandWest.shippingBinPosition.Y == y)
|
||||||
|
{
|
||||||
|
return ("Shipping Bin", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return (null, null) if no relevant object is found
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about tiles at a given coordinate in a VolcanoDungeon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dungeon">The VolcanoDungeon to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name of the tile and the CATEGORY, or (null, null) if no relevant tile is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetVolcanoDungeonInfo(VolcanoDungeon dungeon, int x, int y)
|
||||||
|
{
|
||||||
|
if (dungeon.IsCooledLava(x, y))
|
||||||
|
{
|
||||||
|
return ("Cooled lava", CATEGORY.WaterTiles);
|
||||||
|
}
|
||||||
|
else if (StardewValley.Monsters.LavaLurk.IsLavaTile(dungeon, x, y))
|
||||||
|
{
|
||||||
|
return ("Lava", CATEGORY.WaterTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in a named IslandLocation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="islandLocation">The named IslandLocation to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetNamedIslandLocationInfo(IslandLocation islandLocation, int x, int y)
|
||||||
|
{
|
||||||
|
object locationType = islandLocation is not null and IslandLocation ? islandLocation.Name ?? "Undefined Island Location" : islandLocation!.GetType();
|
||||||
|
|
||||||
|
// Implement specific logic for named IslandLocations here, if necessary
|
||||||
|
|
||||||
|
// Unimplemented locations are logged.
|
||||||
|
// Check if the location has already been logged
|
||||||
|
if (!loggedLocations.Contains(locationType))
|
||||||
|
{
|
||||||
|
// Log the message
|
||||||
|
MainClass.DebugLog($"Called GetNamedIslandLocationInfo with unimplemented IslandLocation of type {islandLocation.GetType()} and name {islandLocation.Name}");
|
||||||
|
|
||||||
|
// Add the location to the HashSet to prevent logging it again
|
||||||
|
loggedLocations.Add(locationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the name of the IslandGemBird based on its item index value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bird">The IslandGemBird instance.</param>
|
||||||
|
/// <returns>A string representing the name of the IslandGemBird.</returns>
|
||||||
|
private static String GetGemBirdName(IslandGemBird bird)
|
||||||
|
{
|
||||||
|
// Use a switch expression to return the appropriate bird name based on the item index value
|
||||||
|
return bird.itemIndex.Value switch
|
||||||
|
{
|
||||||
|
60 => "Emerald Gem Bird",
|
||||||
|
62 => "Aquamarine Gem Bird",
|
||||||
|
64 => "Ruby Gem Bird",
|
||||||
|
66 => "Amethyst Gem Bird",
|
||||||
|
68 => "Topaz Gem Bird",
|
||||||
|
_ => "Gem Bird", // Default case for when the item index does not match any of the specified values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parrot perch information at the specified tile coordinates in the given island location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The x-coordinate of the tile to check.</param>
|
||||||
|
/// <param name="y">The y-coordinate of the tile to check.</param>
|
||||||
|
/// <param name="islandLocation">The IslandLocation where the parrot perch might be found.</param>
|
||||||
|
/// <returns>A string containing the parrot perch information if a parrot perch is found at the specified tile; null if no parrot perch is found.</returns>
|
||||||
|
private static string? GetParrotPerchAtTile(IslandLocation islandLocation, int x, int y)
|
||||||
|
{
|
||||||
|
// Use LINQ to find the first parrot perch at the specified tile (x, y) coordinates
|
||||||
|
var foundPerch = islandLocation.parrotUpgradePerches.FirstOrDefault(perch => perch.tilePosition.Value.Equals(new Point(x, y)));
|
||||||
|
|
||||||
|
// If a parrot perch was found at the specified tile coordinates
|
||||||
|
if (foundPerch != null)
|
||||||
|
{
|
||||||
|
string toSpeak = $"Parrot required nuts {foundPerch.requiredNuts.Value}";
|
||||||
|
|
||||||
|
// Return appropriate string based on the current state of the parrot perch
|
||||||
|
return foundPerch.currentState.Value switch
|
||||||
|
{
|
||||||
|
StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Idle => foundPerch.IsAvailable() ? toSpeak : "Empty parrot perch",
|
||||||
|
StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.StartBuilding => "Parrots started building request",
|
||||||
|
StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Building => "Parrots building request",
|
||||||
|
StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Complete => "Request Completed",
|
||||||
|
_ => toSpeak,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no parrot perch was found, return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in an IslandLocation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="islandLocation">The IslandLocation to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetIslandLocationInfo(IslandLocation islandLocation, int x, int y)
|
||||||
|
{
|
||||||
|
var nutTracker = Game1.player.team.collectedNutTracker;
|
||||||
|
string? parrot = GetParrotPerchAtTile(islandLocation, x, y);
|
||||||
|
if (islandLocation.IsBuriedNutLocation(new Point(x, y)) && !nutTracker.ContainsKey($"Buried_{islandLocation.Name}_{x}_{y}"))
|
||||||
|
{
|
||||||
|
return ("Diggable spot", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
else if (islandLocation.locationGemBird.Value is IslandGemBird bird && ((int)bird.position.X / Game1.tileSize) == x && ((int)bird.position.Y / Game1.tileSize) == y)
|
||||||
|
{
|
||||||
|
return (GetGemBirdName(bird), CATEGORY.NPCs);
|
||||||
|
}
|
||||||
|
else if (parrot != null)
|
||||||
|
{
|
||||||
|
return (parrot, CATEGORY.Buildings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return islandLocation switch
|
||||||
|
{
|
||||||
|
IslandNorth islandNorth => GetIslandNorthInfo(islandNorth, x, y),
|
||||||
|
IslandWest islandWest => GetIslandWestInfo(islandWest, x, y),
|
||||||
|
VolcanoDungeon dungeon => GetVolcanoDungeonInfo(dungeon, x, y),
|
||||||
|
_ => GetNamedIslandLocationInfo(islandLocation, x, y)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the value of the "Action" property from the Buildings layer tile at the given coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryMuseum">The LibraryMuseum containing the tile.</param>
|
||||||
|
/// <param name="x">The x-coordinate of the tile.</param>
|
||||||
|
/// <param name="y">The y-coordinate of the tile.</param>
|
||||||
|
/// <returns>The value of the "Action" property as a string, or null if the property is not found.</returns>
|
||||||
|
private static string? GetTileActionPropertyValue(LibraryMuseum libraryMuseum, int x, int y)
|
||||||
|
{
|
||||||
|
xTile.Tiles.Tile tile = libraryMuseum.map.GetLayer("Buildings").PickTile(new xTile.Dimensions.Location(x * 64, y * 64), Game1.viewport.Size);
|
||||||
|
return tile.Properties.TryGetValue("Action", out xTile.ObjectModel.PropertyValue? value) ? value.ToString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables, NPCs, or other features at a given coordinate in a LibraryMuseum.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryMuseum">The LibraryMuseum to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetLibraryMuseumInfo(LibraryMuseum libraryMuseum, int x, int y)
|
||||||
|
{
|
||||||
|
if (libraryMuseum.museumPieces.TryGetValue(new Vector2(x, y), out int museumPiece))
|
||||||
|
{
|
||||||
|
string displayName = Game1.objectInformation[museumPiece].Split('/')[0];
|
||||||
|
return ($"{displayName} showcase", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
|
||||||
|
int booksFound = Game1.netWorldState.Value.LostBooksFound.Value;
|
||||||
|
string? action = libraryMuseum.doesTileHaveProperty(x, y, "Action", "Buildings");
|
||||||
|
if (action != null && action.Contains("Notes"))
|
||||||
|
{
|
||||||
|
string? actionPropertyValue = GetTileActionPropertyValue(libraryMuseum, x, y);
|
||||||
|
|
||||||
|
if (actionPropertyValue != null)
|
||||||
|
{
|
||||||
|
int which = Convert.ToInt32(actionPropertyValue.Split(' ')[1]);
|
||||||
|
if (booksFound >= which)
|
||||||
|
{
|
||||||
|
string message = Game1.content.LoadString("Strings\\Notes:" + which);
|
||||||
|
return ($"{message.Split('\n')[0]} Book", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
return ($"Lost Book", CATEGORY.Others);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves information about interactables or other features at a given coordinate in a Town.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="town">The Town to search.</param>
|
||||||
|
/// <param name="x">The x-coordinate to search.</param>
|
||||||
|
/// <param name="y">The y-coordinate to search.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the object found, or (null, null) if no relevant object is found.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetTownInfo(Town town, int x, int y)
|
||||||
|
{
|
||||||
|
if (SpecialOrder.IsSpecialOrdersBoardUnlocked() && x == 62 && y == 93)
|
||||||
|
{
|
||||||
|
return ("Special quest board", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the feeding bench information for barns and coops.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentLocation">The current GameLocation instance.</param>
|
||||||
|
/// <param name="x">The x coordinate of the tile.</param>
|
||||||
|
/// <param name="y">The y coordinate of the tile.</param>
|
||||||
|
/// <returns>A tuple of (string? name, CATEGORY? category) for the feeding bench, or null if not applicable.</returns>
|
||||||
|
private static (string? name, CATEGORY? category)? GetFeedingBenchInfo(GameLocation currentLocation, int x, int y)
|
||||||
|
{
|
||||||
|
string locationName = currentLocation.Name;
|
||||||
|
|
||||||
|
if (FeedingBenchBounds.TryGetValue(locationName, out var bounds) && x >= bounds.minX && x <= bounds.maxX && y == bounds.y)
|
||||||
|
{
|
||||||
|
(string? name, CATEGORY category) = TileInfo.getObjectAtTile(x, y, currentLocation, true);
|
||||||
|
return (name?.Contains("hay", StringComparison.OrdinalIgnoreCase) == true ? "Feeding Bench" : "Empty Feeding Bench", category);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets information about the current location by its name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentLocation">The current GameLocation instance.</param>
|
||||||
|
/// <param name="x">The x coordinate of the tile.</param>
|
||||||
|
/// <param name="y">The y coordinate of the tile.</param>
|
||||||
|
/// <returns>A tuple of (string? name, CATEGORY? category) for the object in the location, or null if not applicable.</returns>
|
||||||
|
private static (string? name, CATEGORY? category) GetLocationByNameInfo(GameLocation currentLocation, int x, int y)
|
||||||
|
{
|
||||||
|
object locationType = currentLocation is not null and GameLocation ? currentLocation.Name ?? "Undefined GameLocation" : currentLocation!.GetType(); string locationName = currentLocation.Name ?? "";
|
||||||
|
if (locationName.Contains("coop", StringComparison.OrdinalIgnoreCase) || locationName.Contains("barn", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var feedingBenchInfo = GetFeedingBenchInfo(currentLocation, x, y);
|
||||||
|
if (feedingBenchInfo.HasValue)
|
||||||
|
{
|
||||||
|
return feedingBenchInfo.Value;
|
||||||
|
} // else if something other than feeding benches in barns and coops...
|
||||||
|
} //else if something other than barns and coops...
|
||||||
|
|
||||||
|
// Unimplemented locations are logged.
|
||||||
|
// Check if the location has already been logged
|
||||||
|
if (!loggedLocations.Contains(locationType))
|
||||||
|
{
|
||||||
|
// Log the message
|
||||||
|
MainClass.DebugLog($"Called GetLocationByNameInfo with unimplemented GameLocation of type {currentLocation.GetType()} and name {currentLocation.Name}");
|
||||||
|
|
||||||
|
// Add the location to the HashSet to prevent logging it again
|
||||||
|
loggedLocations.Add(locationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the dynamic tile information for the given coordinates in the specified location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The x-coordinate of the tile.</param>
|
||||||
|
/// <param name="y">The y-coordinate of the tile.</param>
|
||||||
|
/// <param name="currentLocation">The current GameLocation instance.</param>
|
||||||
|
/// <param name="lessInfo">An optional boolean to return less detailed information. Defaults to false.</param>
|
||||||
|
/// <returns>A tuple containing the name and CATEGORY of the dynamic tile, or null values if not found.</returns>
|
||||||
|
public static (string? name, CATEGORY? category) GetDynamicTileAt(int x, int y, GameLocation currentLocation, bool lessInfo = false)
|
||||||
|
{
|
||||||
|
// Check for panning spots
|
||||||
|
if (currentLocation.orePanPoint.Value != Point.Zero && currentLocation.orePanPoint.Value == new Point(x, y))
|
||||||
|
{
|
||||||
|
return ("panning spot", CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
// Check if the current location has an event
|
||||||
|
else if (currentLocation.currentEvent is not null)
|
||||||
|
{
|
||||||
|
string eventName = currentLocation.currentEvent.FestivalName;
|
||||||
|
// Attempt to retrieve the nested dictionary for the event name from the EventInteractables dictionary
|
||||||
|
if (EventInteractables.TryGetValue(eventName, out var coordinateDictionary))
|
||||||
|
{
|
||||||
|
// Attempt to retrieve the interactable value from the nested dictionary using the coordinates (x, y) as the key
|
||||||
|
if (coordinateDictionary.TryGetValue((x, y), value: out var interactable))
|
||||||
|
{
|
||||||
|
// If the interactable value is found, return the corresponding category and interactable name
|
||||||
|
return (interactable, CATEGORY.Interactables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve dynamic tile information based on the current location type
|
||||||
|
return currentLocation switch
|
||||||
|
{
|
||||||
|
Beach beach => GetBeachInfo(beach, x, y),
|
||||||
|
BoatTunnel boatTunnel => GetBoatTunnelInfo(boatTunnel, x, y),
|
||||||
|
CommunityCenter communityCenter => GetCommunityCenterInfo(communityCenter, x, y),
|
||||||
|
Farm farm => GetFarmInfo(farm, x, y),
|
||||||
|
FarmHouse farmHouse => GetFarmHouseInfo(farmHouse, x, y),
|
||||||
|
Forest forest => GetForestInfo(forest, x, y),
|
||||||
|
IslandFarmHouse islandFarmHouse => GetIslandFarmHouseInfo(islandFarmHouse, x, y),
|
||||||
|
IslandLocation islandLocation => GetIslandLocationInfo(islandLocation, x, y),
|
||||||
|
LibraryMuseum libraryMuseum => GetLibraryMuseumInfo(libraryMuseum, x, y),
|
||||||
|
Town town => GetTownInfo(town, x, y),
|
||||||
|
_ => GetLocationByNameInfo(currentLocation, x, y)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,9 @@ namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
public class Radar
|
public class Radar
|
||||||
{
|
{
|
||||||
private List<Vector2> closed;
|
private readonly List<Vector2> closed;
|
||||||
private List<Furniture> furnitures;
|
private readonly List<Furniture> furnitures;
|
||||||
private List<NPC> npcs;
|
private readonly List<NPC> npcs;
|
||||||
public List<string> exclusions;
|
public List<string> exclusions;
|
||||||
private List<string> temp_exclusions;
|
private List<string> temp_exclusions;
|
||||||
public List<string> focus;
|
public List<string> focus;
|
||||||
|
@ -94,10 +94,10 @@ namespace stardew_access.Features
|
||||||
public Dictionary<Vector2, (string, string)> SearchNearbyTiles(Vector2 center, int limit, bool playSound = true)
|
public Dictionary<Vector2, (string, string)> SearchNearbyTiles(Vector2 center, int limit, bool playSound = true)
|
||||||
{
|
{
|
||||||
var currentLocation = Game1.currentLocation;
|
var currentLocation = Game1.currentLocation;
|
||||||
Dictionary<Vector2, (string, string)> detectedTiles = new Dictionary<Vector2, (string, string)>();
|
Dictionary<Vector2, (string, string)> detectedTiles = new();
|
||||||
|
|
||||||
Queue<Vector2> toSearch = new Queue<Vector2>();
|
Queue<Vector2> toSearch = new();
|
||||||
HashSet<Vector2> searched = new HashSet<Vector2>();
|
HashSet<Vector2> searched = new();
|
||||||
int[] dirX = { -1, 0, 1, 0 };
|
int[] dirX = { -1, 0, 1, 0 };
|
||||||
int[] dirY = { 0, 1, 0, -1 };
|
int[] dirY = { 0, 1, 0, -1 };
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
Vector2 dir = new Vector2(item.X + dirX[i], item.Y + dirY[i]);
|
Vector2 dir = new(item.X + dirX[i], item.Y + dirY[i]);
|
||||||
|
|
||||||
if (isValid(dir, center, searched, limit))
|
if (isValid(dir, center, searched, limit))
|
||||||
{
|
{
|
||||||
|
@ -144,12 +144,12 @@ namespace stardew_access.Features
|
||||||
var watch = new Stopwatch();
|
var watch = new Stopwatch();
|
||||||
watch.Start();
|
watch.Start();
|
||||||
var currentLocation = Game1.currentLocation;
|
var currentLocation = Game1.currentLocation;
|
||||||
Dictionary<Vector2, (string, string)> detectedTiles = new Dictionary<Vector2, (string, string)>();
|
Dictionary<Vector2, (string, string)> detectedTiles = new();
|
||||||
Vector2 position = Vector2.Zero;
|
Vector2 position = Vector2.Zero;
|
||||||
(bool, string? name, string category) tileInfo;
|
(bool, string? name, string category) tileInfo;
|
||||||
|
|
||||||
Queue<Vector2> toSearch = new Queue<Vector2>();
|
Queue<Vector2> toSearch = new();
|
||||||
HashSet<Vector2> searched = new HashSet<Vector2>();
|
HashSet<Vector2> searched = new();
|
||||||
int[] dirX = { -1, 0, 1, 0 };
|
int[] dirX = { -1, 0, 1, 0 };
|
||||||
int[] dirY = { 0, 1, 0, -1 };
|
int[] dirY = { 0, 1, 0, -1 };
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -160,7 +160,6 @@ namespace stardew_access.Features
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
var elapsedMs = watch.ElapsedMilliseconds;
|
var elapsedMs = watch.ElapsedMilliseconds;
|
||||||
MainClass.DebugLog($"Search init duration: {elapsedMs}");
|
MainClass.DebugLog($"Search init duration: {elapsedMs}");
|
||||||
elapsedMs = 0;
|
|
||||||
watch.Reset();
|
watch.Reset();
|
||||||
watch.Start();
|
watch.Start();
|
||||||
while (toSearch.Count > 0)
|
while (toSearch.Count > 0)
|
||||||
|
@ -177,7 +176,7 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
Vector2 dir = new Vector2(item.X + dirX[i], item.Y + dirY[i]);
|
Vector2 dir = new(item.X + dirX[i], item.Y + dirY[i]);
|
||||||
|
|
||||||
if (!searched.Contains(dir) && (TileInfo.isWarpPointAtTile((int)dir.X, (int)dir.Y, currentLocation) || currentLocation.isTileOnMap(dir)))
|
if (!searched.Contains(dir) && (TileInfo.isWarpPointAtTile((int)dir.X, (int)dir.Y, currentLocation) || currentLocation.isTileOnMap(dir)))
|
||||||
{
|
{
|
||||||
|
@ -216,14 +215,13 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
public (bool, string? name, string category) CheckTile(Vector2 position, GameLocation currentLocation, bool lessInfo = false)
|
public (bool, string? name, string category) CheckTile(Vector2 position, GameLocation currentLocation, bool lessInfo = false)
|
||||||
{
|
{
|
||||||
(string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position, currentLocation, lessInfo);
|
(string? name, CATEGORY? category) = TileInfo.getNameWithCategoryAtTile(position, currentLocation, lessInfo);
|
||||||
if (tileDetail.name == null)
|
if (name == null)
|
||||||
return (false, null, CATEGORY.Others.ToString());
|
return (false, null, CATEGORY.Others.ToString());
|
||||||
|
|
||||||
if (tileDetail.category == null)
|
category ??= CATEGORY.Others;
|
||||||
tileDetail.category = CATEGORY.Others;
|
|
||||||
|
|
||||||
return (true, tileDetail.name, tileDetail.category.ToString());
|
return (true, name, category.ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,13 +255,12 @@ namespace stardew_access.Features
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position, currentLocation);
|
(string? name, CATEGORY? category) = TileInfo.getNameWithCategoryAtTile(position, currentLocation);
|
||||||
if (tileDetail.name != null)
|
if (name != null)
|
||||||
{
|
{
|
||||||
if (tileDetail.category == null)
|
category ??= CATEGORY.Others;
|
||||||
tileDetail.category = CATEGORY.Others;
|
|
||||||
|
|
||||||
PlaySoundAt(position, tileDetail.name, tileDetail.category, currentLocation);
|
PlaySoundAt(position, name, category, currentLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Text.Json;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
using static stardew_access.Features.Utils;
|
||||||
|
|
||||||
namespace stardew_access.Features
|
namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
|
@ -18,10 +19,7 @@ namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_instance == null)
|
_instance ??= new StaticTiles();
|
||||||
{
|
|
||||||
_instance = new StaticTiles();
|
|
||||||
}
|
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +72,7 @@ namespace stardew_access.Features
|
||||||
/// </list>
|
/// </list>
|
||||||
/// Additional lambda functions can be added as needed.
|
/// Additional lambda functions can be added as needed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private static readonly Dictionary<string, Func<string, string, bool>> conditionals = new Dictionary<string, Func<string, string, bool>>
|
private static readonly Dictionary<string, Func<string, string, bool>> conditionals = new()
|
||||||
{
|
{
|
||||||
["Farm"] = (conditionType, uniqueModId) =>
|
["Farm"] = (conditionType, uniqueModId) =>
|
||||||
{
|
{
|
||||||
|
@ -122,36 +120,6 @@ namespace stardew_access.Features
|
||||||
SetupTilesDicts();
|
SetupTilesDicts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Loads a JSON file from the specified file name in the assets folder.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The name of the JSON file to load.</param>
|
|
||||||
/// <returns>A <see cref="JsonElement"/> containing the deserialized JSON data, or default if an error occurs.</returns>
|
|
||||||
private static JsonElement LoadJsonFile(string fileName)
|
|
||||||
{
|
|
||||||
string filePath = Path.Combine(MainClass.ModHelper!.DirectoryPath, "assets", fileName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string json = File.ReadAllText(filePath);
|
|
||||||
return JsonSerializer.Deserialize<JsonElement>(json);
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException ex)
|
|
||||||
{
|
|
||||||
MainClass.ErrorLog($"{fileName} file not found: {ex.Message}");
|
|
||||||
}
|
|
||||||
catch (JsonException ex)
|
|
||||||
{
|
|
||||||
MainClass.ErrorLog($"Error parsing {fileName}: {ex.Message}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
MainClass.ErrorLog($"An error occurred while initializing {fileName}: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the static and custom tile files.
|
/// Loads the static and custom tile files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -497,10 +465,7 @@ namespace stardew_access.Features
|
||||||
/// <returns>A tuple containing the tile's name and optionally its category. If the tile is not found, the name will be null and the category will be CATEGORY.Others if requested.</returns>
|
/// <returns>A tuple containing the tile's name and optionally its category. If the tile is not found, the name will be null and the category will be CATEGORY.Others if requested.</returns>
|
||||||
private static (string? name, CATEGORY? category) GetTileInfoAt(int x, int y, string? currentLocationName = null, bool includeCategory = false)
|
private static (string? name, CATEGORY? category) GetTileInfoAt(int x, int y, string? currentLocationName = null, bool includeCategory = false)
|
||||||
{
|
{
|
||||||
if (currentLocationName == null)
|
currentLocationName ??= Game1.currentLocation.Name;
|
||||||
{
|
|
||||||
currentLocationName = Game1.currentLocation.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customTilesDataDict != null && customTilesDataDict.TryGetValue(currentLocationName, out var customLocationDict))
|
if (customTilesDataDict != null && customTilesDataDict.TryGetValue(currentLocationName, out var customLocationDict))
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,13 +5,14 @@ using StardewValley.Buildings;
|
||||||
using StardewValley.Locations;
|
using StardewValley.Locations;
|
||||||
using StardewValley.Objects;
|
using StardewValley.Objects;
|
||||||
using StardewValley.TerrainFeatures;
|
using StardewValley.TerrainFeatures;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace stardew_access.Features
|
namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
public class TileInfo
|
public class TileInfo
|
||||||
{
|
{
|
||||||
public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom", "statue of endless fortune", "statue of perfection", "crab pot" };
|
private static readonly string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom", "statue of endless fortune", "statue of perfection", "crab pot" };
|
||||||
private static readonly Dictionary<int, string> ResourceClumpNames = new Dictionary<int, string>
|
private static readonly Dictionary<int, string> ResourceClumpNames = new()
|
||||||
{
|
{
|
||||||
{ 600, "Large Stump" },
|
{ 600, "Large Stump" },
|
||||||
{ 602, "Hollow Log" },
|
{ 602, "Hollow Log" },
|
||||||
|
@ -29,25 +30,24 @@ namespace stardew_access.Features
|
||||||
///<summary>Returns the name of the object at tile alongwith it's category's name</summary>
|
///<summary>Returns the name of the object at tile alongwith it's category's name</summary>
|
||||||
public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile, GameLocation? currentLocation)
|
public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile, GameLocation? currentLocation)
|
||||||
{
|
{
|
||||||
(string? name, CATEGORY? category) tileDetail = getNameWithCategoryAtTile(tile, currentLocation);
|
(string? name, CATEGORY? category) = getNameWithCategoryAtTile(tile, currentLocation);
|
||||||
|
|
||||||
if (tileDetail.category == null)
|
category ??= CATEGORY.Others;
|
||||||
tileDetail.category = CATEGORY.Others;
|
|
||||||
|
|
||||||
return (tileDetail.name, tileDetail.category.ToString());
|
return (name, category.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>Returns the name of the object at tile</summary>
|
///<summary>Returns the name of the object at tile</summary>
|
||||||
public static string? getNameAtTile(Vector2 tile, GameLocation? currentLocation = null)
|
public static string? GetNameAtTile(Vector2 tile, GameLocation? currentLocation = null)
|
||||||
{
|
{
|
||||||
if (currentLocation is null) currentLocation = Game1.currentLocation;
|
currentLocation ??= Game1.currentLocation;
|
||||||
return getNameWithCategoryAtTile(tile, currentLocation).name;
|
return getNameWithCategoryAtTile(tile, currentLocation).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>Returns the name of the object at tile alongwith it's category</summary>
|
///<summary>Returns the name of the object at tile alongwith it's category</summary>
|
||||||
public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile, GameLocation? currentLocation, bool lessInfo = false)
|
public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile, GameLocation? currentLocation, bool lessInfo = false)
|
||||||
{
|
{
|
||||||
if (currentLocation is null) currentLocation = Game1.currentLocation;
|
currentLocation ??= Game1.currentLocation;
|
||||||
int x = (int)tile.X;
|
int x = (int)tile.X;
|
||||||
int y = (int)tile.Y;
|
int y = (int)tile.Y;
|
||||||
string? toReturn = null;
|
string? toReturn = null;
|
||||||
|
@ -59,13 +59,12 @@ namespace stardew_access.Features
|
||||||
var terrainFeature = currentLocation.terrainFeatures.FieldDict;
|
var terrainFeature = currentLocation.terrainFeatures.FieldDict;
|
||||||
string? door = getDoorAtTile(x, y, currentLocation);
|
string? door = getDoorAtTile(x, y, currentLocation);
|
||||||
string? warp = getWarpPointAtTile(x, y, currentLocation);
|
string? warp = getWarpPointAtTile(x, y, currentLocation);
|
||||||
(CATEGORY? category, string? name) dynamicTile = getDynamicTilesInfo(x, y, currentLocation, lessInfo);
|
(string? name, CATEGORY? category) dynamicTile = DynamicTiles.GetDynamicTileAt(x, y, currentLocation, lessInfo);
|
||||||
string? junimoBundle = getJunimoBundleAt(x, y, currentLocation);
|
string? junimoBundle = getJunimoBundleAt(x, y, currentLocation);
|
||||||
string? resourceClump = getResourceClumpAtTile(x, y, currentLocation, lessInfo);
|
string? resourceClump = getResourceClumpAtTile(x, y, currentLocation, lessInfo);
|
||||||
string? farmAnimal = getFarmAnimalAt(currentLocation, x, y);
|
string? farmAnimal = getFarmAnimalAt(currentLocation, x, y);
|
||||||
string? parrot = currentLocation is IslandLocation islandLocation ? getParrotPerchAtTile(x, y, islandLocation) : null;
|
|
||||||
(string? name, CATEGORY category) staticTile = StaticTiles.GetStaticTileInfoAtWithCategory(x, y, currentLocation.Name);
|
(string? name, CATEGORY category) staticTile = StaticTiles.GetStaticTileInfoAtWithCategory(x, y, currentLocation.Name);
|
||||||
string? bush = getBushAtTile(x, y, currentLocation, lessInfo);
|
string? bush = GetBushAtTile(x, y, currentLocation, lessInfo);
|
||||||
|
|
||||||
if (currentLocation.isCharacterAtTile(tile) is NPC npc)
|
if (currentLocation.isCharacterAtTile(tile) is NPC npc)
|
||||||
{
|
{
|
||||||
|
@ -90,16 +89,6 @@ namespace stardew_access.Features
|
||||||
toReturn = dynamicTile.name;
|
toReturn = dynamicTile.name;
|
||||||
category = dynamicTile.category;
|
category = dynamicTile.category;
|
||||||
}
|
}
|
||||||
else if (currentLocation is VolcanoDungeon && ((VolcanoDungeon)currentLocation).IsCooledLava(x, y) && !lessInfo)
|
|
||||||
{
|
|
||||||
toReturn = "Cooled lava";
|
|
||||||
category = CATEGORY.WaterTiles;
|
|
||||||
}
|
|
||||||
else if (currentLocation is VolcanoDungeon && StardewValley.Monsters.LavaLurk.IsLavaTile((VolcanoDungeon)currentLocation, x, y) && !lessInfo)
|
|
||||||
{
|
|
||||||
toReturn = "Lava";
|
|
||||||
category = CATEGORY.WaterTiles;
|
|
||||||
}
|
|
||||||
else if (currentLocation.isObjectAtTile(x, y))
|
else if (currentLocation.isObjectAtTile(x, y))
|
||||||
{
|
{
|
||||||
(string? name, CATEGORY? category) obj = getObjectAtTile(x, y, currentLocation, lessInfo);
|
(string? name, CATEGORY? category) obj = getObjectAtTile(x, y, currentLocation, lessInfo);
|
||||||
|
@ -162,11 +151,6 @@ namespace stardew_access.Features
|
||||||
toReturn = "Elevator";
|
toReturn = "Elevator";
|
||||||
category = CATEGORY.Doors;
|
category = CATEGORY.Doors;
|
||||||
}
|
}
|
||||||
else if (parrot != null)
|
|
||||||
{
|
|
||||||
toReturn = parrot;
|
|
||||||
category = CATEGORY.Buildings;
|
|
||||||
}
|
|
||||||
else if (junimoBundle != null)
|
else if (junimoBundle != null)
|
||||||
{
|
{
|
||||||
toReturn = junimoBundle;
|
toReturn = junimoBundle;
|
||||||
|
@ -179,7 +163,7 @@ namespace stardew_access.Features
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NetCollection<Debris> droppedItems = currentLocation.debris;
|
NetCollection<Debris> droppedItems = currentLocation.debris;
|
||||||
int droppedItemsCount = droppedItems.Count();
|
int droppedItemsCount = droppedItems.Count;
|
||||||
if (droppedItemsCount > 0)
|
if (droppedItemsCount > 0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < droppedItemsCount; i++)
|
for (int i = 0; i < droppedItemsCount; i++)
|
||||||
|
@ -212,7 +196,7 @@ namespace stardew_access.Features
|
||||||
return (toReturn, category);
|
return (toReturn, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? getBushAtTile(int x, int y, GameLocation currentLocation, bool lessInfo = false)
|
public static string? GetBushAtTile(int x, int y, GameLocation currentLocation, bool lessInfo = false)
|
||||||
{
|
{
|
||||||
string? toReturn = null;
|
string? toReturn = null;
|
||||||
Bush? bush = (Bush)currentLocation.getLargeTerrainFeatureAt(x, y);
|
Bush? bush = (Bush)currentLocation.getLargeTerrainFeatureAt(x, y);
|
||||||
|
@ -269,7 +253,7 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
public static string? getJunimoBundleAt(int x, int y, GameLocation currentLocation)
|
public static string? getJunimoBundleAt(int x, int y, GameLocation currentLocation)
|
||||||
{
|
{
|
||||||
string? name = null;
|
string? name;
|
||||||
if (currentLocation is CommunityCenter communityCenter)
|
if (currentLocation is CommunityCenter communityCenter)
|
||||||
{
|
{
|
||||||
name = (x, y) switch
|
name = (x, y) switch
|
||||||
|
@ -302,7 +286,7 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
public static bool isCollidingAtTile(int x, int y, GameLocation currentLocation)
|
public static bool isCollidingAtTile(int x, int y, GameLocation currentLocation)
|
||||||
{
|
{
|
||||||
Rectangle rect = new Rectangle(x * 64 + 1, y * 64 + 1, 62, 62);
|
Rectangle rect = new(x * 64 + 1, y * 64 + 1, 62, 62);
|
||||||
|
|
||||||
/* Reference
|
/* Reference
|
||||||
// Check whether the position is a warp point, if so then return false, sometimes warp points are 1 tile off the map for example in coops and barns
|
// Check whether the position is a warp point, if so then return false, sometimes warp points are 1 tile off the map for example in coops and barns
|
||||||
|
@ -335,7 +319,7 @@ namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
if (currentLocation is null) return false;
|
if (currentLocation is null) return false;
|
||||||
|
|
||||||
int warpsCount = currentLocation.warps.Count();
|
int warpsCount = currentLocation.warps.Count;
|
||||||
for (int i = 0; i < warpsCount; i++)
|
for (int i = 0; i < warpsCount; i++)
|
||||||
{
|
{
|
||||||
if (currentLocation.warps[i].X == x && currentLocation.warps[i].Y == y) return true;
|
if (currentLocation.warps[i].X == x && currentLocation.warps[i].Y == y) return true;
|
||||||
|
@ -389,441 +373,132 @@ namespace stardew_access.Features
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Retrieves the name and category of the terrain feature at the given tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="terrain">A reference to the terrain feature to be checked.</param>
|
||||||
/// <param name="y"></param>
|
/// <returns>A tuple containing the name and category of the terrain feature at the tile.</returns>
|
||||||
/// <returns>category: This is the category of the tile. Default to Furnitures.
|
|
||||||
/// <br/>name: This is the name of the tile. Default to null if the tile tile has nothing on it.</returns>
|
|
||||||
public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y, GameLocation currentLocation, bool lessInfo = false)
|
|
||||||
{
|
|
||||||
if (currentLocation.orePanPoint.Value != Point.Zero && currentLocation.orePanPoint.Value == new Point(x, y))
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "panning spot");
|
|
||||||
}
|
|
||||||
else if (currentLocation is Farm farm)
|
|
||||||
{
|
|
||||||
if (farm.GetMainMailboxPosition().X == x && farm.GetMainMailboxPosition().Y == y)
|
|
||||||
return (CATEGORY.Interactables, "Mail box");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Building building = farm.getBuildingAt(new Vector2(x, y));
|
|
||||||
if (building is not null)
|
|
||||||
{
|
|
||||||
string name = building.buildingType.Value;
|
|
||||||
|
|
||||||
// Prepend fish name for fish ponds
|
|
||||||
if (building is FishPond fishPond)
|
|
||||||
{
|
|
||||||
if (fishPond.fishType.Value >= 0)
|
|
||||||
{
|
|
||||||
name = $"{Game1.objectInformation[fishPond.fishType.Value].Split('/')[4]} {name}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect doors, input slots, etc.
|
|
||||||
if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y)
|
|
||||||
return (CATEGORY.Doors, name + " Door");
|
|
||||||
else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y)
|
|
||||||
return (CATEGORY.Doors, name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed"));
|
|
||||||
else if (building.tileX.Value == x && building.tileY.Value == y)
|
|
||||||
return (CATEGORY.Buildings, name);
|
|
||||||
else if (building is Mill && (building.tileX.Value + 1) == x && (building.tileY.Value + 1) == y)
|
|
||||||
return (CATEGORY.Buildings, name + " input");
|
|
||||||
else if (building is Mill && (building.tileX.Value + 3) == x && (building.tileY.Value + 1) == y)
|
|
||||||
return (CATEGORY.Buildings, name + " output");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Buildings, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.currentEvent is not null)
|
|
||||||
{
|
|
||||||
string event_name = currentLocation.currentEvent.FestivalName;
|
|
||||||
if (event_name == "Egg Festival" && x == 21 && y == 55)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Egg Festival Shop");
|
|
||||||
}
|
|
||||||
else if (event_name == "Flower Dance" && x == 28 && y == 37)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Flower Dance Shop");
|
|
||||||
}
|
|
||||||
else if (event_name == "Luau" && x == 35 && y == 13)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Soup Pot");
|
|
||||||
}
|
|
||||||
else if (event_name == "Spirit's Eve" && x == 25 && y == 49)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Spirit's Eve Shop");
|
|
||||||
}
|
|
||||||
else if (event_name == "Stardew Valley Fair")
|
|
||||||
{
|
|
||||||
if (x == 16 && y == 52)
|
|
||||||
return (CATEGORY.Interactables, "Stardew Valley Fair Shop");
|
|
||||||
else if (x == 23 && y == 62)
|
|
||||||
return (CATEGORY.Interactables, "Slingshot Game");
|
|
||||||
else if (x == 34 && y == 65)
|
|
||||||
return (CATEGORY.Interactables, "Purchase Star Tokens");
|
|
||||||
else if (x == 33 && y == 70)
|
|
||||||
return (CATEGORY.Interactables, "The Wheel");
|
|
||||||
else if (x == 23 && y == 70)
|
|
||||||
return (CATEGORY.Interactables, "Fishing Challenge");
|
|
||||||
else if (x == 47 && y == 87)
|
|
||||||
return (CATEGORY.Interactables, "Fortune Teller");
|
|
||||||
else if (x == 38 && y == 59)
|
|
||||||
return (CATEGORY.Interactables, "Grange Display");
|
|
||||||
else if (x == 30 && y == 56)
|
|
||||||
return (CATEGORY.Interactables, "Strength Game");
|
|
||||||
else if (x == 26 && y == 33)
|
|
||||||
return (CATEGORY.Interactables, "Free Burgers");
|
|
||||||
}
|
|
||||||
else if (event_name == "Festival of Ice" && x == 55 && y == 31)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Travelling Cart");
|
|
||||||
}
|
|
||||||
else if (event_name == "Feast of the Winter Star" && x == 18 && y == 61)
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Feast of the Winter Star Shop");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (currentLocation is Town)
|
|
||||||
{
|
|
||||||
if (SpecialOrder.IsSpecialOrdersBoardUnlocked() && x == 62 && y == 93)
|
|
||||||
return (CATEGORY.Interactables, "Special quest board");
|
|
||||||
}
|
|
||||||
else if (currentLocation is FarmHouse farmHouse)
|
|
||||||
{
|
|
||||||
if (farmHouse.upgradeLevel >= 1)
|
|
||||||
if (farmHouse.getKitchenStandingSpot().X == x && (farmHouse.getKitchenStandingSpot().Y - 1) == y)
|
|
||||||
return (CATEGORY.Interactables, "Stove");
|
|
||||||
else if ((farmHouse.getKitchenStandingSpot().X + 1) == x && (farmHouse.getKitchenStandingSpot().Y - 1) == y)
|
|
||||||
return (CATEGORY.Others, "Sink");
|
|
||||||
else if (farmHouse.fridgePosition.X == x && farmHouse.fridgePosition.Y == y)
|
|
||||||
return (CATEGORY.Interactables, "Fridge");
|
|
||||||
}
|
|
||||||
else if (currentLocation is IslandFarmHouse islandFarmHouse)
|
|
||||||
{
|
|
||||||
if ((islandFarmHouse.fridgePosition.X - 2) == x && islandFarmHouse.fridgePosition.Y == y)
|
|
||||||
return (CATEGORY.Interactables, "Stove");
|
|
||||||
else if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y)
|
|
||||||
return (CATEGORY.Others, "Sink");
|
|
||||||
else if (islandFarmHouse.fridgePosition.X == x && islandFarmHouse.fridgePosition.Y == y)
|
|
||||||
return (CATEGORY.Interactables, "Fridge");
|
|
||||||
}
|
|
||||||
else if (currentLocation is Forest forest)
|
|
||||||
{
|
|
||||||
if (forest.travelingMerchantDay && x == 27 && y == 11)
|
|
||||||
return (CATEGORY.Interactables, "Travelling Cart");
|
|
||||||
else if (forest.log != null && x == 2 && y == 7)
|
|
||||||
return (CATEGORY.Interactables, "Log");
|
|
||||||
else if (forest.log == null && x == 0 && y == 7)
|
|
||||||
return (CATEGORY.Doors, "Secret Woods Entrance");
|
|
||||||
}
|
|
||||||
else if (currentLocation is Beach beach)
|
|
||||||
{
|
|
||||||
if (MainClass.ModHelper == null)
|
|
||||||
return (null, null);
|
|
||||||
|
|
||||||
if (MainClass.ModHelper.Reflection.GetField<NPC>(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y))
|
|
||||||
{
|
|
||||||
return (CATEGORY.NPCs, "Old Mariner");
|
|
||||||
}
|
|
||||||
else if (x == 58 && y == 13)
|
|
||||||
{
|
|
||||||
if (!beach.bridgeFixed.Value)
|
|
||||||
return (CATEGORY.Interactables, "Repair Bridge");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Bridges, "Bridge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation is CommunityCenter communityCenter)
|
|
||||||
{
|
|
||||||
if (communityCenter.missedRewardsChestVisible.Value && x == 22 && y == 10)
|
|
||||||
return (CATEGORY.Containers, "Missed Rewards Chest");
|
|
||||||
}
|
|
||||||
else if (currentLocation is BoatTunnel)
|
|
||||||
{
|
|
||||||
if (x == 4 && y == 9)
|
|
||||||
return (CATEGORY.Interactables, ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatFixed")) ? "Repair " : "") + "Ticket Machine");
|
|
||||||
else if (x == 6 && y == 8)
|
|
||||||
return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull")) ? "Repair " : "") + "Boat Hull");
|
|
||||||
else if (x == 8 && y == 9)
|
|
||||||
return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? "Repair " : "") + "Boat Anchor");
|
|
||||||
}
|
|
||||||
else if (currentLocation is IslandLocation islandLocation)
|
|
||||||
{
|
|
||||||
var nutTracker = Game1.player.team.collectedNutTracker;
|
|
||||||
if (islandLocation.IsBuriedNutLocation(new Point(x, y)) && !nutTracker.ContainsKey($"Buried_{islandLocation.Name}_{x}_{y}"))
|
|
||||||
{
|
|
||||||
return (CATEGORY.Interactables, "Diggable spot");
|
|
||||||
}
|
|
||||||
else if (islandLocation.locationGemBird.Value is IslandGemBird bird && ((int)bird.position.X / Game1.tileSize) == x && ((int)bird.position.Y / Game1.tileSize) == y)
|
|
||||||
{
|
|
||||||
return (CATEGORY.NPCs, GetGemBirdName(bird));
|
|
||||||
}
|
|
||||||
else if (currentLocation is IslandWest islandWest)
|
|
||||||
{
|
|
||||||
if ((islandWest.shippingBinPosition.X == x || (islandWest.shippingBinPosition.X + 1) == x) && islandWest.shippingBinPosition.Y == y)
|
|
||||||
return (CATEGORY.Interactables, "Shipping Bin");
|
|
||||||
}
|
|
||||||
else if (currentLocation is IslandNorth islandNorth)
|
|
||||||
{
|
|
||||||
if (islandNorth.traderActivated.Value && x == 36 && y == 71)
|
|
||||||
return (CATEGORY.Interactables, "Island Trader");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("coop", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 6 && x <= 9 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name is not null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("coop2", StringComparison.OrdinalIgnoreCase) || currentLocation.Name.Equals("big coop", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 6 && x <= 13 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name is not null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("coop3", StringComparison.OrdinalIgnoreCase) || currentLocation.Name.Equals("deluxe coop", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 6 && x <= 17 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name is not null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("barn", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 8 && x <= 11 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name != null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("barn2", StringComparison.OrdinalIgnoreCase) || currentLocation.Name.Equals("big barn", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 8 && x <= 15 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name is not null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation.Name.Equals("barn3", StringComparison.OrdinalIgnoreCase) || currentLocation.Name.Equals("deluxe barn", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (x >= 8 && x <= 19 && y == 3)
|
|
||||||
{
|
|
||||||
(string? name, CATEGORY category) bench = getObjectAtTile(x, y, currentLocation, true);
|
|
||||||
if (bench.name is not null && bench.name.Contains("hay", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return (CATEGORY.Others, "Feeding Bench");
|
|
||||||
else
|
|
||||||
return (CATEGORY.Others, "Empty Feeding Bench");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentLocation is LibraryMuseum libraryMuseum)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<Vector2, int> pair in libraryMuseum.museumPieces.Pairs)
|
|
||||||
{
|
|
||||||
if (pair.Key.X == x && pair.Key.Y == y)
|
|
||||||
{
|
|
||||||
string displayName = Game1.objectInformation[pair.Value].Split('/')[0];
|
|
||||||
return (CATEGORY.Interactables, $"{displayName} showcase");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int booksFound = Game1.netWorldState.Value.LostBooksFound.Value;
|
|
||||||
for (int x1 = 0; x1 < libraryMuseum.map.Layers[0].LayerWidth; x1++)
|
|
||||||
{
|
|
||||||
for (int y1 = 0; y1 < libraryMuseum.map.Layers[0].LayerHeight; y1++)
|
|
||||||
{
|
|
||||||
if (x != x1 || y != y1) continue;
|
|
||||||
|
|
||||||
if (libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings") != null && libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings").Contains("Notes"))
|
|
||||||
{
|
|
||||||
int key = Convert.ToInt32(libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings").Split(' ')[1]);
|
|
||||||
xTile.Tiles.Tile tile = libraryMuseum.map.GetLayer("Buildings").PickTile(new xTile.Dimensions.Location(x * 64, y * 64), Game1.viewport.Size);
|
|
||||||
string? action = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tile.Properties.TryGetValue("Action", out xTile.ObjectModel.PropertyValue? value);
|
|
||||||
if (value != null) action = value.ToString();
|
|
||||||
}
|
|
||||||
catch (System.Exception e)
|
|
||||||
{
|
|
||||||
MainClass.ErrorLog($"Cannot get action value at x:{x} y:{y} in LibraryMuseum");
|
|
||||||
MainClass.ErrorLog(e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action != null)
|
|
||||||
{
|
|
||||||
string[] actionParams = action.Split(' ');
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int which = Convert.ToInt32(actionParams[1]);
|
|
||||||
if (booksFound >= which)
|
|
||||||
{
|
|
||||||
string message = Game1.content.LoadString("Strings\\Notes:" + which);
|
|
||||||
return (CATEGORY.Interactables, $"{message.Split('\n')[0]} Book");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (System.Exception e)
|
|
||||||
{
|
|
||||||
MainClass.ErrorLog(e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (CATEGORY.Others, $"Lost Book");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (string? name, CATEGORY category) getTerrainFeatureAtTile(Netcode.NetRef<TerrainFeature> terrain)
|
public static (string? name, CATEGORY category) getTerrainFeatureAtTile(Netcode.NetRef<TerrainFeature> terrain)
|
||||||
{
|
{
|
||||||
string? toReturn = null;
|
// Get the terrain feature from the reference
|
||||||
CATEGORY category = CATEGORY.Others;
|
var terrainFeature = terrain.Get();
|
||||||
|
|
||||||
if (terrain.Get() is HoeDirt dirt)
|
// Check if the terrain feature is HoeDirt
|
||||||
|
if (terrainFeature is HoeDirt dirt)
|
||||||
{
|
{
|
||||||
toReturn = getHoeDirtDetail(dirt);
|
return (getHoeDirtDetail(dirt), CATEGORY.Crops);
|
||||||
category = CATEGORY.Crops;
|
|
||||||
}
|
}
|
||||||
else if (terrain.Get() is CosmeticPlant)
|
// Check if the terrain feature is a CosmeticPlant
|
||||||
|
else if (terrainFeature is CosmeticPlant cosmeticPlant)
|
||||||
{
|
{
|
||||||
category = CATEGORY.Furnitures;
|
string toReturn = cosmeticPlant.textureName().ToLower();
|
||||||
CosmeticPlant cosmeticPlant = (CosmeticPlant)terrain.Get();
|
|
||||||
toReturn = cosmeticPlant.textureName().ToLower();
|
|
||||||
|
|
||||||
if (toReturn.Contains("terrain"))
|
toReturn = toReturn.Replace("terrain", "").Replace("feature", "");
|
||||||
toReturn.Replace("terrain", "");
|
|
||||||
|
|
||||||
if (toReturn.Contains("feature"))
|
return (toReturn, CATEGORY.Furnitures);
|
||||||
toReturn.Replace("feature", "");
|
|
||||||
}
|
}
|
||||||
else if (terrain.Get() is Flooring && MainClass.Config.ReadFlooring)
|
// Check if the terrain feature is Flooring
|
||||||
|
else if (terrainFeature is Flooring flooring && MainClass.Config.ReadFlooring)
|
||||||
{
|
{
|
||||||
category = CATEGORY.Flooring;
|
|
||||||
Flooring flooring = (Flooring)terrain.Get();
|
|
||||||
bool isPathway = flooring.isPathway.Get();
|
bool isPathway = flooring.isPathway.Get();
|
||||||
bool isSteppingStone = flooring.isSteppingStone.Get();
|
bool isSteppingStone = flooring.isSteppingStone.Get();
|
||||||
|
string toReturn = isPathway ? "Pathway" : (isSteppingStone ? "Stepping Stone" : "Flooring");
|
||||||
|
|
||||||
toReturn = "Flooring";
|
return (toReturn, CATEGORY.Flooring);
|
||||||
|
|
||||||
if (isPathway)
|
|
||||||
toReturn = "Pathway";
|
|
||||||
|
|
||||||
if (isSteppingStone)
|
|
||||||
toReturn = "Stepping Stone";
|
|
||||||
}
|
}
|
||||||
else if (terrain.Get() is FruitTree)
|
// Check if the terrain feature is a FruitTree
|
||||||
|
else if (terrainFeature is FruitTree fruitTree)
|
||||||
{
|
{
|
||||||
category = CATEGORY.Trees;
|
return (getFruitTree(fruitTree), CATEGORY.Trees);
|
||||||
toReturn = getFruitTree((FruitTree)terrain.Get());
|
|
||||||
}
|
}
|
||||||
else if (terrain.Get() is Grass)
|
// Check if the terrain feature is Grass
|
||||||
|
else if (terrainFeature is Grass)
|
||||||
{
|
{
|
||||||
category = CATEGORY.Debris;
|
return ("Grass", CATEGORY.Debris);
|
||||||
toReturn = "Grass";
|
|
||||||
}
|
}
|
||||||
else if (terrain.Get() is Tree)
|
// Check if the terrain feature is a Tree
|
||||||
|
else if (terrainFeature is Tree tree)
|
||||||
{
|
{
|
||||||
category = CATEGORY.Trees;
|
return (getTree(tree), CATEGORY.Trees);
|
||||||
toReturn = getTree((Tree)terrain.Get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (toReturn, category);
|
return (null, CATEGORY.Others);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the detail about the HoeDirt i.e. soil, plant, etc.
|
/// Retrieves a detailed description of HoeDirt, including its soil, plant, and other relevant information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dirt">The HoeDirt to be checked</param>
|
/// <param name="dirt">The HoeDirt object to get details for.</param>
|
||||||
/// <param name="ignoreIfEmpty">Ignores returning `soil` if empty</param>
|
/// <param name="ignoreIfEmpty">If true, the method will return an empty string for empty soil; otherwise, it will return "Soil".</param>
|
||||||
/// <returns>The details about the given HoeDirt</returns>
|
/// <returns>A string representing the details of the provided HoeDirt object.</returns>
|
||||||
public static string getHoeDirtDetail(HoeDirt dirt, bool ignoreIfEmpty = false)
|
public static string getHoeDirtDetail(HoeDirt dirt, bool ignoreIfEmpty = false)
|
||||||
{
|
{
|
||||||
string detail;
|
// Use StringBuilder for efficient string manipulation
|
||||||
|
StringBuilder detail = new();
|
||||||
if (dirt.crop != null && !dirt.crop.forageCrop.Value)
|
|
||||||
{
|
|
||||||
string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0];
|
|
||||||
detail = $"{cropName}";
|
|
||||||
|
|
||||||
|
// Calculate isWatered and isFertilized only once
|
||||||
bool isWatered = dirt.state.Value == HoeDirt.watered;
|
bool isWatered = dirt.state.Value == HoeDirt.watered;
|
||||||
bool isHarvestable = dirt.readyForHarvest();
|
|
||||||
bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer;
|
bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer;
|
||||||
|
|
||||||
|
// Check the watered status and append it to the detail string
|
||||||
if (isWatered && MainClass.Config.WateredToggle)
|
if (isWatered && MainClass.Config.WateredToggle)
|
||||||
detail = "Watered " + detail;
|
detail.Append("Watered ");
|
||||||
else if (!isWatered && !MainClass.Config.WateredToggle)
|
else if (!isWatered && !MainClass.Config.WateredToggle)
|
||||||
detail = "Unwatered " + detail;
|
detail.Append("Unwatered ");
|
||||||
|
|
||||||
|
// Check if the dirt is fertilized and append it to the detail string
|
||||||
if (isFertilized)
|
if (isFertilized)
|
||||||
detail = "Fertilized " + detail;
|
detail.Append("Fertilized ");
|
||||||
|
|
||||||
if (isHarvestable)
|
// Check if the dirt has a crop
|
||||||
detail = "Harvestable " + detail;
|
if (dirt.crop != null)
|
||||||
|
|
||||||
if (dirt.crop.dead.Value)
|
|
||||||
detail = "Dead " + detail;
|
|
||||||
}
|
|
||||||
else if (dirt.crop != null && dirt.crop.forageCrop.Value)
|
|
||||||
{
|
{
|
||||||
detail = dirt.crop.whichForageCrop.Value switch
|
// Handle forage crops
|
||||||
|
if (dirt.crop.forageCrop.Value)
|
||||||
|
{
|
||||||
|
detail.Append(dirt.crop.whichForageCrop.Value switch
|
||||||
{
|
{
|
||||||
1 => "Spring onion",
|
1 => "Spring onion",
|
||||||
2 => "Ginger",
|
2 => "Ginger",
|
||||||
_ => "Forageable crop"
|
_ => "Forageable crop"
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
else
|
else // Handle non-forage crops
|
||||||
{
|
{
|
||||||
detail = (ignoreIfEmpty) ? "" : "Soil";
|
// Append the crop name to the detail string
|
||||||
bool isWatered = dirt.state.Value == HoeDirt.watered;
|
string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0];
|
||||||
bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer;
|
detail.Append(cropName);
|
||||||
|
|
||||||
if (isWatered && MainClass.Config.WateredToggle)
|
// Check if the crop is harvestable and prepend it to the detail string
|
||||||
detail = "Watered " + detail;
|
if (dirt.readyForHarvest())
|
||||||
else if (!isWatered && !MainClass.Config.WateredToggle)
|
detail.Insert(0, "Harvestable ");
|
||||||
detail = "Unwatered " + detail;
|
|
||||||
|
|
||||||
if (isFertilized)
|
// Check if the crop is dead and prepend it to the detail string
|
||||||
detail = "Fertilized " + detail;
|
if (dirt.crop.dead.Value)
|
||||||
|
detail.Insert(0, "Dead ");
|
||||||
}
|
}
|
||||||
return detail;
|
}
|
||||||
|
else if (!ignoreIfEmpty) // If there's no crop and ignoreIfEmpty is false, append "Soil" to the detail string
|
||||||
|
{
|
||||||
|
detail.Append("Soil");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return detail.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the fruit tree's display name based on its growth stage and fruit index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fruitTree">The FruitTree object to get the name for.</param>
|
||||||
|
/// <returns>The fruit tree's display name.</returns>
|
||||||
public static string getFruitTree(FruitTree fruitTree)
|
public static string getFruitTree(FruitTree fruitTree)
|
||||||
{
|
{
|
||||||
int stage = fruitTree.growthStage.Value;
|
int stage = fruitTree.growthStage.Value;
|
||||||
int fruitIndex = fruitTree.indexOfFruit.Get();
|
int fruitIndex = fruitTree.indexOfFruit.Get();
|
||||||
|
|
||||||
|
// Get the base name of the fruit tree from the object information
|
||||||
string toReturn = Game1.objectInformation[fruitIndex].Split('/')[0];
|
string toReturn = Game1.objectInformation[fruitIndex].Split('/')[0];
|
||||||
|
|
||||||
|
// Append the growth stage description to the fruit tree name
|
||||||
if (stage == 0)
|
if (stage == 0)
|
||||||
toReturn = $"{toReturn} seed";
|
toReturn = $"{toReturn} seed";
|
||||||
else if (stage == 1)
|
else if (stage == 1)
|
||||||
|
@ -835,20 +510,25 @@ namespace stardew_access.Features
|
||||||
else if (stage >= 4)
|
else if (stage >= 4)
|
||||||
toReturn = $"{toReturn} tree";
|
toReturn = $"{toReturn} tree";
|
||||||
|
|
||||||
|
// If there are fruits on the tree, prepend "Harvestable" to the name
|
||||||
if (fruitTree.fruitsOnTree.Value > 0)
|
if (fruitTree.fruitsOnTree.Value > 0)
|
||||||
toReturn = $"Harvestable {toReturn}";
|
toReturn = $"Harvestable {toReturn}";
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the tree's display name based on its type and growth stage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tree">The Tree object to get the name for.</param>
|
||||||
|
/// <returns>The tree's display name.</returns>
|
||||||
public static string getTree(Tree tree)
|
public static string getTree(Tree tree)
|
||||||
{
|
{
|
||||||
int treeType = tree.treeType.Value;
|
int treeType = tree.treeType.Value;
|
||||||
int treeStage = tree.growthStage.Value;
|
int treeStage = tree.growthStage.Value;
|
||||||
string treeName = "tree";
|
|
||||||
string seedName = "";
|
string seedName = "";
|
||||||
|
|
||||||
// Return with the name if it's one of the 3 special trees
|
// Handle special tree types and return their names
|
||||||
switch (treeType)
|
switch (treeType)
|
||||||
{
|
{
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -860,14 +540,16 @@ namespace stardew_access.Features
|
||||||
return "Mushroom Tree";
|
return "Mushroom Tree";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the seed name for the tree type
|
||||||
if (treeType <= 3)
|
if (treeType <= 3)
|
||||||
seedName = Game1.objectInformation[308 + treeType].Split('/')[0];
|
seedName = Game1.objectInformation[308 + treeType].Split('/')[0];
|
||||||
else if (treeType == 8)
|
else if (treeType == 8)
|
||||||
seedName = Game1.objectInformation[292].Split('/')[0];
|
seedName = Game1.objectInformation[292].Split('/')[0];
|
||||||
|
|
||||||
|
// Determine the tree name and growth stage description
|
||||||
if (treeStage >= 1)
|
if (treeStage >= 1)
|
||||||
{
|
{
|
||||||
|
string treeName;
|
||||||
switch (seedName.ToLower())
|
switch (seedName.ToLower())
|
||||||
{
|
{
|
||||||
case "mahogany seed":
|
case "mahogany seed":
|
||||||
|
@ -887,6 +569,7 @@ namespace stardew_access.Features
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append the growth stage description to the tree name
|
||||||
if (treeStage == 1)
|
if (treeStage == 1)
|
||||||
treeName = $"{treeName} sprout";
|
treeName = $"{treeName} sprout";
|
||||||
else if (treeStage == 2)
|
else if (treeStage == 2)
|
||||||
|
@ -899,6 +582,7 @@ namespace stardew_access.Features
|
||||||
return treeName;
|
return treeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the seed name if the tree is at stage 0
|
||||||
return seedName;
|
return seedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1262,7 +946,7 @@ namespace stardew_access.Features
|
||||||
public static string? getDoorAtTile(int x, int y, GameLocation currentLocation)
|
public static string? getDoorAtTile(int x, int y, GameLocation currentLocation)
|
||||||
{
|
{
|
||||||
// Create a Point object from the given tile coordinates
|
// Create a Point object from the given tile coordinates
|
||||||
Point tilePoint = new Point(x, y);
|
Point tilePoint = new(x, y);
|
||||||
|
|
||||||
// Access the doorList in the current location
|
// Access the doorList in the current location
|
||||||
StardewValley.Network.NetPointDictionary<string, Netcode.NetString> doorList = currentLocation.doors;
|
StardewValley.Network.NetPointDictionary<string, Netcode.NetString> doorList = currentLocation.doors;
|
||||||
|
@ -1339,64 +1023,5 @@ namespace stardew_access.Features
|
||||||
// No matching stump found
|
// No matching stump found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parrot perch information at the specified tile coordinates in the given island location.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="x">The x-coordinate of the tile to check.</param>
|
|
||||||
/// <param name="y">The y-coordinate of the tile to check.</param>
|
|
||||||
/// <param name="islandLocation">The IslandLocation where the parrot perch might be found.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// A string containing the parrot perch information if a parrot perch is found at the specified tile;
|
|
||||||
/// null if no parrot perch is found.
|
|
||||||
/// </returns>
|
|
||||||
public static string? getParrotPerchAtTile(int x, int y, IslandLocation islandLocation)
|
|
||||||
{
|
|
||||||
// Use LINQ to find the first parrot perch at the specified tile (x, y) coordinates
|
|
||||||
var foundPerch = islandLocation.parrotUpgradePerches.FirstOrDefault(perch => perch.tilePosition.Value.Equals(new Point(x, y)));
|
|
||||||
|
|
||||||
// If a parrot perch was found at the specified tile coordinates
|
|
||||||
if (foundPerch != null)
|
|
||||||
{
|
|
||||||
string toSpeak = $"Parrot required nuts {foundPerch.requiredNuts.Value}";
|
|
||||||
|
|
||||||
// Return appropriate string based on the current state of the parrot perch
|
|
||||||
switch (foundPerch.currentState.Value)
|
|
||||||
{
|
|
||||||
case StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Idle:
|
|
||||||
return foundPerch.IsAvailable() ? toSpeak : "Empty parrot perch";
|
|
||||||
case StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.StartBuilding:
|
|
||||||
return "Parrots started building request";
|
|
||||||
case StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Building:
|
|
||||||
return "Parrots building request";
|
|
||||||
case StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Complete:
|
|
||||||
return "Request Completed";
|
|
||||||
default:
|
|
||||||
return toSpeak;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no parrot perch was found, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the name of the IslandGemBird based on its item index value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bird">The IslandGemBird instance.</param>
|
|
||||||
/// <returns>A string representing the name of the IslandGemBird.</returns>
|
|
||||||
public static String GetGemBirdName(IslandGemBird bird)
|
|
||||||
{
|
|
||||||
// Use a switch expression to return the appropriate bird name based on the item index value
|
|
||||||
return bird.itemIndex.Value switch
|
|
||||||
{
|
|
||||||
60 => "Emerald Gem Bird",
|
|
||||||
62 => "Aquamarine Gem Bird",
|
|
||||||
64 => "Ruby Gem Bird",
|
|
||||||
66 => "Amethyst Gem Bird",
|
|
||||||
68 => "Topaz Gem Bird",
|
|
||||||
_ => "Gem Bird", // Default case for when the item index does not match any of the specified values
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ namespace stardew_access.Features
|
||||||
if (!tryMoveTileView(delta)) return;
|
if (!tryMoveTileView(delta)) return;
|
||||||
Vector2 position = this.GetTileCursorPosition();
|
Vector2 position = this.GetTileCursorPosition();
|
||||||
Vector2 tile = this.GetViewingTile();
|
Vector2 tile = this.GetViewingTile();
|
||||||
String? name = TileInfo.getNameAtTile(tile);
|
String? name = TileInfo.GetNameAtTile(tile);
|
||||||
|
|
||||||
// Prepend the player's name if the viewing tile is occupied by the player itself
|
// Prepend the player's name if the viewing tile is occupied by the player itself
|
||||||
if (CurrentPlayer.PositionX == tile.X && CurrentPlayer.PositionY == tile.Y)
|
if (CurrentPlayer.PositionX == tile.X && CurrentPlayer.PositionY == tile.Y)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Text.Json;
|
||||||
namespace stardew_access.Features
|
namespace stardew_access.Features
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -33,7 +34,7 @@ namespace stardew_access.Features
|
||||||
|
|
||||||
public static IReadOnlyDictionary<string, CATEGORY> Categories => _categories;
|
public static IReadOnlyDictionary<string, CATEGORY> Categories => _categories;
|
||||||
|
|
||||||
private static readonly Dictionary<string, CATEGORY> _categories = new Dictionary<string, CATEGORY>(StringComparer.OrdinalIgnoreCase)
|
private static readonly Dictionary<string, CATEGORY> _categories = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{"farmer", new CATEGORY("farmer")},
|
{"farmer", new CATEGORY("farmer")},
|
||||||
{"animal", new CATEGORY("animal")},
|
{"animal", new CATEGORY("animal")},
|
||||||
|
@ -122,11 +123,44 @@ namespace stardew_access.Features
|
||||||
public static CATEGORY Machines => FromString("machine");
|
public static CATEGORY Machines => FromString("machine");
|
||||||
public static CATEGORY Bridges => FromString("bridge");
|
public static CATEGORY Bridges => FromString("bridge");
|
||||||
public static CATEGORY DroppedItems => FromString("dropped item");
|
public static CATEGORY DroppedItems => FromString("dropped item");
|
||||||
public static CATEGORY Others => FromString("other");
|
public static CATEGORY Others => FromString("other");
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MachineState
|
public enum MachineState
|
||||||
{
|
{
|
||||||
Ready, Busy, Waiting
|
Ready, Busy, Waiting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a JSON file from the specified file name in the assets folder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the JSON file to load.</param>
|
||||||
|
/// <returns>A <see cref="JsonElement"/> containing the deserialized JSON data, or default if an error occurs.</returns>
|
||||||
|
public static JsonElement LoadJsonFile(string fileName)
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(MainClass.ModHelper!.DirectoryPath, "assets", fileName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText(filePath);
|
||||||
|
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException ex)
|
||||||
|
{
|
||||||
|
MainClass.ErrorLog($"{fileName} file not found: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
MainClass.ErrorLog($"Error parsing {fileName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MainClass.ErrorLog($"An error occurred while initializing {fileName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"Egg Festival": {
|
||||||
|
"21,55": "Egg Festival Shop"
|
||||||
|
},
|
||||||
|
"Flower Dance": {
|
||||||
|
"28,37": "Flower Dance Shop"
|
||||||
|
},
|
||||||
|
"Luau": {
|
||||||
|
"35,13": "Soup Pot"
|
||||||
|
},
|
||||||
|
"Spirit's Eve": {
|
||||||
|
"25,49": "Spirit's Eve Shop"
|
||||||
|
},
|
||||||
|
"Stardew Valley Fair": {
|
||||||
|
"16,52": "Stardew Valley Fair Shop",
|
||||||
|
"23,62": "Slingshot Game",
|
||||||
|
"34,65": "Purchase Star Tokens",
|
||||||
|
"33,70": "The Wheel",
|
||||||
|
"23,70": "Fishing Challenge",
|
||||||
|
"47,87": "Fortune Teller",
|
||||||
|
"38,59": "Grange Display",
|
||||||
|
"30,56": "Strength Game",
|
||||||
|
"26,33": "Free Burgers"
|
||||||
|
},
|
||||||
|
"Festival of Ice": {
|
||||||
|
"55,31": "Travelling Cart"
|
||||||
|
},
|
||||||
|
"Feast of the Winter Star": {
|
||||||
|
"18,61": "Feast of the Winter Star Shop"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue