808 lines
39 KiB
C#
808 lines
39 KiB
C#
using Microsoft.Xna.Framework;
|
|
using StardewValley;
|
|
using StardewValley.Buildings;
|
|
using StardewValley.Locations;
|
|
using static stardew_access.Features.Utils;
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
// Check if the player has received the specified mail or not
|
|
bool HasMail(string mail) => Game1.MasterPlayer.hasOrWillReceiveMail(mail);
|
|
|
|
// If the position matches one of the interactable elements in the boat tunnel
|
|
if ((x, y) == (4, 9) || (x, y) == (6, 8) || (x, y) == (8, 9))
|
|
{
|
|
string mail = (x, y) switch
|
|
{
|
|
(4, 9) => "willyBoatFixed",
|
|
(6, 8) => "willyBoatHull",
|
|
(8, 9) => "willyBoatAnchor",
|
|
_ => throw new InvalidOperationException("Unexpected (x, y) values"),
|
|
};
|
|
|
|
string itemName = (x, y) switch
|
|
{
|
|
(4, 9) => "Ticket Machine",
|
|
(6, 8) => "Boat Hull",
|
|
(8, 9) => "Boat Anchor",
|
|
_ => throw new InvalidOperationException("Unexpected (x, y) values"),
|
|
};
|
|
|
|
CATEGORY category = (x, y) == (4, 9) ? CATEGORY.Interactables : (!HasMail(mail) ? CATEGORY.Interactables : CATEGORY.Decor);
|
|
|
|
return ((!HasMail(mail) ? "Repair " : "") + itemName, category);
|
|
}
|
|
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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, lessInfo);
|
|
}
|
|
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
// 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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
// 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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
if (!lessInfo)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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, lessInfo),
|
|
IslandWest islandWest => GetIslandWestInfo(islandWest, x, y, lessInfo),
|
|
VolcanoDungeon dungeon => GetVolcanoDungeonInfo(dungeon, x, y, lessInfo),
|
|
_ => GetNamedIslandLocationInfo(islandLocation, x, y, lessInfo)
|
|
};
|
|
}
|
|
|
|
/// <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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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, lessInfo);
|
|
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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(currentLocation, x, y, 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>
|
|
/// <param name="lessInfo">Optional. If true, returns information only if the tile coordinates match the resource clump's origin. Default is false.</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, bool lessInfo = false)
|
|
{
|
|
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="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>
|
|
/// <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(GameLocation currentLocation, int x, int y, 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, lessInfo),
|
|
BoatTunnel boatTunnel => GetBoatTunnelInfo(boatTunnel, x, y, lessInfo),
|
|
CommunityCenter communityCenter => GetCommunityCenterInfo(communityCenter, x, y, lessInfo),
|
|
Farm farm => GetFarmInfo(farm, x, y, lessInfo),
|
|
FarmHouse farmHouse => GetFarmHouseInfo(farmHouse, x, y, lessInfo),
|
|
Forest forest => GetForestInfo(forest, x, y, lessInfo),
|
|
IslandFarmHouse islandFarmHouse => GetIslandFarmHouseInfo(islandFarmHouse, x, y, lessInfo),
|
|
IslandLocation islandLocation => GetIslandLocationInfo(islandLocation, x, y, lessInfo),
|
|
LibraryMuseum libraryMuseum => GetLibraryMuseumInfo(libraryMuseum, x, y, lessInfo),
|
|
Town town => GetTownInfo(town, x, y, lessInfo),
|
|
_ => GetLocationByNameInfo(currentLocation, x, y, lessInfo)
|
|
};
|
|
}
|
|
}
|
|
}
|