523 lines
25 KiB
C#
523 lines
25 KiB
C#
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework.Input;
|
|
using UIInfoSuite.Extensions;
|
|
using StardewModdingAPI;
|
|
using StardewModdingAPI.Events;
|
|
using StardewValley;
|
|
using StardewValley.Menus;
|
|
using StardewValley.Quests;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace UIInfoSuite.UIElements
|
|
{
|
|
class LocationOfTownsfolk : IDisposable
|
|
{
|
|
#region Members
|
|
private List<NPC> _townsfolk = new List<NPC>();
|
|
private List<OptionsCheckbox> _checkboxes = new List<OptionsCheckbox>();
|
|
private const int SocialPanelWidth = 190;
|
|
private const int SocialPanelXOffset = 160;
|
|
private SocialPage _socialPage;
|
|
private String[] _friendNames;
|
|
private readonly IDictionary<String, String> _options;
|
|
private readonly IModHelper _helper;
|
|
|
|
private static readonly Dictionary<String, KeyValuePair<int, int>> _mapLocations = new Dictionary<string, KeyValuePair<int, int>>()
|
|
{
|
|
{ "HarveyRoom", new KeyValuePair<int, int>(677, 304) },
|
|
{ "BathHouse_Pool", new KeyValuePair<int, int>(576, 60) },
|
|
{ "WizardHouseBasement", new KeyValuePair<int, int>(196, 352) },
|
|
{ "BugLand", new KeyValuePair<int, int>(0, 0) },
|
|
{ "Desert", new KeyValuePair<int, int>(60, 92) },
|
|
{ "Cellar", new KeyValuePair<int, int>(0, 0) },
|
|
{ "JojaMart", new KeyValuePair<int, int>(872, 280) },
|
|
{ "Tent", new KeyValuePair<int, int>(784, 128) },
|
|
{ "HaleyHouse", new KeyValuePair<int, int>(652, 408) },
|
|
{ "Hospital", new KeyValuePair<int, int>(677, 304) },
|
|
{ "FarmHouse", new KeyValuePair<int, int>(470, 260) },
|
|
{ "Farm", new KeyValuePair<int, int>(470, 260) },
|
|
{ "ScienceHouse", new KeyValuePair<int, int>(732, 148) },
|
|
{ "ManorHouse", new KeyValuePair<int, int>(768, 395) },
|
|
{ "AdventureGuild", new KeyValuePair<int, int>(0, 0) },
|
|
{ "SeedShop", new KeyValuePair<int, int>(696, 296) },
|
|
{ "Blacksmith", new KeyValuePair<int, int>(852, 388) },
|
|
{ "JoshHouse", new KeyValuePair<int, int>(740, 320) },
|
|
{ "SandyHouse", new KeyValuePair<int, int>(40, 40) },
|
|
{ "Tunnel", new KeyValuePair<int, int>(0, 0) },
|
|
{ "CommunityCenter", new KeyValuePair<int, int>(692, 204) },
|
|
{ "Backwoods", new KeyValuePair<int, int>(460, 156) },
|
|
{ "ElliottHouse", new KeyValuePair<int, int>(826, 550) },
|
|
{ "SebastianRoom", new KeyValuePair<int, int>(732, 148) },
|
|
{ "BathHouse_Entry", new KeyValuePair<int, int>(576, 60) },
|
|
{ "Greenhouse", new KeyValuePair<int, int>(0, 0) },
|
|
{ "Sewer", new KeyValuePair<int, int>(380, 596) },
|
|
{ "WizardHouse", new KeyValuePair<int, int>(196, 352) },
|
|
{ "Trailer", new KeyValuePair<int, int>(780, 360) },
|
|
{ "Forest", new KeyValuePair<int, int>(80, 272) },
|
|
{ "Woods", new KeyValuePair<int, int>(100, 272) },
|
|
{ "WitchSwamp", new KeyValuePair<int, int>(0, 0) },
|
|
{ "ArchaeologyHouse", new KeyValuePair<int, int>(892, 416) },
|
|
{ "FishShop", new KeyValuePair<int, int>(844, 608) },
|
|
{ "Saloon", new KeyValuePair<int, int>(714, 354) },
|
|
{ "LeahHouse", new KeyValuePair<int, int>(452, 436) },
|
|
{ "Town", new KeyValuePair<int, int>(680, 360) },
|
|
{ "Mountain", new KeyValuePair<int, int>(762, 154) },
|
|
{ "BusStop", new KeyValuePair<int, int>(516, 224) },
|
|
{ "Railroad", new KeyValuePair<int, int>(644, 64) },
|
|
{ "SkullCave", new KeyValuePair<int, int>(0, 0) },
|
|
{ "BathHouse_WomensLocker", new KeyValuePair<int, int>(576, 60) },
|
|
{ "Beach", new KeyValuePair<int, int>(790, 550) },
|
|
{ "BathHouse_MensLocker", new KeyValuePair<int, int>(576, 60) },
|
|
{ "Mine", new KeyValuePair<int, int>(880, 100) },
|
|
{ "WitchHut", new KeyValuePair<int, int>(0, 0) },
|
|
{ "AnimalShop", new KeyValuePair<int, int>(420, 392) },
|
|
{ "SamHouse", new KeyValuePair<int, int>(612, 396) },
|
|
{ "WitchWarpCave", new KeyValuePair<int, int>(0, 0) },
|
|
{ "Club", new KeyValuePair<int, int>(60, 92) }
|
|
};
|
|
|
|
#endregion
|
|
|
|
public LocationOfTownsfolk(IModHelper helper, IDictionary<String, String> options)
|
|
{
|
|
_helper = helper;
|
|
_options = options;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ToggleShowNPCLocationsOnMap(false);
|
|
}
|
|
|
|
public void ToggleShowNPCLocationsOnMap(bool showLocations)
|
|
{
|
|
OnMenuChange(null, null);
|
|
GraphicsEvents.OnPostRenderGuiEvent -= DrawSocialPageOptions;
|
|
GraphicsEvents.OnPostRenderGuiEvent -= DrawNPCLocationsOnMap;
|
|
ControlEvents.MouseChanged -= HandleClickForSocialPage;
|
|
ControlEvents.ControllerButtonPressed -= HandleGamepadPressForSocialPage;
|
|
MenuEvents.MenuChanged -= OnMenuChange;
|
|
|
|
if (showLocations)
|
|
{
|
|
GraphicsEvents.OnPostRenderGuiEvent += DrawSocialPageOptions;
|
|
GraphicsEvents.OnPostRenderGuiEvent += DrawNPCLocationsOnMap;
|
|
ControlEvents.MouseChanged += HandleClickForSocialPage;
|
|
ControlEvents.ControllerButtonPressed += HandleGamepadPressForSocialPage;
|
|
MenuEvents.MenuChanged += OnMenuChange;
|
|
}
|
|
}
|
|
|
|
private void HandleGamepadPressForSocialPage(object sender, EventArgsControllerButtonPressed e)
|
|
{
|
|
if (e.ButtonPressed == Buttons.A)
|
|
CheckSelectedBox();
|
|
}
|
|
|
|
private void OnMenuChange(object sender, EventArgsClickableMenuChanged e)
|
|
{
|
|
if (Game1.activeClickableMenu is GameMenu)
|
|
{
|
|
List<IClickableMenu> clickableMenuList = typeof(GameMenu)
|
|
.GetField("pages", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.GetValue(Game1.activeClickableMenu) as List<IClickableMenu>;
|
|
|
|
foreach (var menu in clickableMenuList)
|
|
{
|
|
if (menu is SocialPage)
|
|
{
|
|
_socialPage = menu as SocialPage;
|
|
_friendNames = (typeof(SocialPage).GetField("names", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(menu) as List<object>)
|
|
.Select(name => name.ToString())
|
|
.ToArray();
|
|
break;
|
|
}
|
|
}
|
|
_townsfolk.Clear();
|
|
foreach (var location in Game1.locations)
|
|
{
|
|
foreach (var npc in location.characters)
|
|
{
|
|
if (Game1.player.friendshipData.ContainsKey(npc.Name))
|
|
_townsfolk.Add(npc);
|
|
}
|
|
}
|
|
_checkboxes.Clear();
|
|
foreach (var friendName in _friendNames)
|
|
{
|
|
int hashCode = friendName.GetHashCode();
|
|
OptionsCheckbox checkbox = new OptionsCheckbox("", hashCode);
|
|
_checkboxes.Add(checkbox);
|
|
|
|
//default to on
|
|
bool optionForThisFriend = true;
|
|
if (!Game1.player.friendshipData.ContainsKey(friendName))
|
|
{
|
|
checkbox.greyedOut = true;
|
|
optionForThisFriend = false;
|
|
}
|
|
else
|
|
{
|
|
String optionValue = _options.SafeGet(hashCode.ToString());
|
|
|
|
if (String.IsNullOrEmpty(optionValue))
|
|
{
|
|
_options[hashCode.ToString()] = optionForThisFriend.ToString();
|
|
}
|
|
else
|
|
{
|
|
optionForThisFriend = optionValue.SafeParseBool();
|
|
}
|
|
}
|
|
checkbox.isChecked = optionForThisFriend;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HandleClickForSocialPage(object sender, EventArgsMouseStateChanged e)
|
|
{
|
|
if (Game1.activeClickableMenu is GameMenu &&
|
|
e.PriorState.LeftButton != ButtonState.Pressed &&
|
|
e.NewState.LeftButton == ButtonState.Pressed)
|
|
{
|
|
CheckSelectedBox();
|
|
}
|
|
}
|
|
|
|
private void CheckSelectedBox()
|
|
{
|
|
if (Game1.activeClickableMenu is GameMenu)
|
|
{
|
|
int slotPosition = (int)typeof(SocialPage)
|
|
.GetField("slotPosition", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.GetValue(_socialPage);
|
|
|
|
for (int i = slotPosition; i < slotPosition + 5; ++i)
|
|
{
|
|
OptionsCheckbox checkbox = _checkboxes[i];
|
|
if (checkbox.bounds.Contains(Game1.getMouseX(), Game1.getMouseY()) &&
|
|
!checkbox.greyedOut)
|
|
{
|
|
checkbox.isChecked = !checkbox.isChecked;
|
|
_options[checkbox.whichOption.ToString()] = checkbox.isChecked.ToString();
|
|
Game1.playSound("drumkit6");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawNPCLocationsOnMap(object sender, EventArgs e)
|
|
{
|
|
if (Game1.activeClickableMenu is GameMenu)
|
|
{
|
|
GameMenu gameMenu = Game1.activeClickableMenu as GameMenu;
|
|
if (gameMenu.currentTab == 3)
|
|
{
|
|
List<String> namesToShow = new List<string>();
|
|
foreach (var character in _townsfolk)
|
|
{
|
|
try
|
|
{
|
|
int hashCode = character.Name.GetHashCode();
|
|
|
|
bool drawCharacter = _options.SafeGet(hashCode.ToString()).SafeParseBool();
|
|
|
|
if (drawCharacter)
|
|
{
|
|
KeyValuePair<int, int> location;
|
|
location = new KeyValuePair<int, int>((int)character.Position.X, (int)character.position.Y);
|
|
String locationName = character.currentLocation?.Name ?? character.DefaultMap;
|
|
|
|
switch (locationName)
|
|
{
|
|
case "Town":
|
|
case "Forest":
|
|
{
|
|
int xStart = 0;
|
|
int yStart = 0;
|
|
int areaWidth = 0;
|
|
int areaHeight = 0;
|
|
|
|
switch (locationName)
|
|
{
|
|
case "Town":
|
|
{
|
|
xStart = 595;
|
|
yStart = 163;
|
|
areaWidth = 345;
|
|
areaHeight = 330;
|
|
break;
|
|
}
|
|
|
|
case "Forest":
|
|
{
|
|
xStart = 183;
|
|
yStart = 378;
|
|
areaWidth = 319;
|
|
areaHeight = 261;
|
|
break;
|
|
}
|
|
}
|
|
xTile.Map map = character.currentLocation.Map;
|
|
|
|
float xScale = (float)areaWidth / (float)map.DisplayWidth;
|
|
float yScale = (float)areaHeight / (float)map.DisplayHeight;
|
|
|
|
float scaledX = character.position.X * xScale;
|
|
float scaledY = character.position.Y * yScale;
|
|
int xPos = (int)scaledX + xStart;
|
|
int yPos = (int)scaledY + yStart;
|
|
location = new KeyValuePair<int, int>(xPos, yPos);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
_mapLocations.TryGetValue(locationName, out location);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//if (character.currentLocation.Name == "Town")
|
|
//{
|
|
// String locationName = character.currentLocation.Name;
|
|
// xTile.Map map = character.currentLocation.Map;
|
|
// int xStart = 595;
|
|
// int yStart = 163;
|
|
// int townWidth = 345;
|
|
// int townHeight = 330;
|
|
|
|
// float xScale = (float)townWidth / (float)map.DisplayWidth;
|
|
// float yScale = (float)townHeight / (float)map.DisplayHeight;
|
|
|
|
// float scaledX = character.position.X * xScale;
|
|
// float scaledY = character.position.Y * yScale;
|
|
// int xPos = (int)scaledX + xStart;
|
|
// int yPos = (int)scaledY + yStart;
|
|
// location = new KeyValuePair<int, int>(xPos, yPos);
|
|
//}
|
|
//else
|
|
//{
|
|
// _mapLocations.TryGetValue(character.currentLocation.name, out location);
|
|
//}
|
|
Rectangle headShot = character.GetHeadShot();
|
|
int xBase = Game1.activeClickableMenu.xPositionOnScreen - 158;
|
|
int yBase = Game1.activeClickableMenu.yPositionOnScreen - 40;
|
|
|
|
int x = xBase + location.Key;
|
|
int y = yBase + location.Value;
|
|
|
|
Color color = character.CurrentDialogue.Count <= 0 ?
|
|
Color.Gray : Color.White;
|
|
ClickableTextureComponent textureComponent =
|
|
new ClickableTextureComponent(
|
|
character.Name,
|
|
new Rectangle(x, y, 0, 0),
|
|
null,
|
|
character.Name,
|
|
character.Sprite.Texture,
|
|
headShot,
|
|
2.3f);
|
|
|
|
float headShotScale = 2f;
|
|
Game1.spriteBatch.Draw(
|
|
character.Sprite.Texture,
|
|
new Vector2(x, y),
|
|
new Rectangle?(headShot),
|
|
color,
|
|
0.0f,
|
|
Vector2.Zero,
|
|
headShotScale,
|
|
SpriteEffects.None,
|
|
1f);
|
|
|
|
int mouseX = Game1.getMouseX();
|
|
int mouseY = Game1.getMouseY();
|
|
|
|
if (mouseX >= x && mouseX <= x + headShot.Width * headShotScale &&
|
|
mouseY >= y && mouseY <= y + headShot.Height * headShotScale)
|
|
{
|
|
namesToShow.Add(character.displayName);
|
|
}
|
|
|
|
foreach (var quest in Game1.player.questLog)
|
|
{
|
|
if (quest.accepted.Value && quest.dailyQuest.Value && !quest.completed.Value)
|
|
{
|
|
bool isQuestTarget = false;
|
|
switch (quest.questType.Value)
|
|
{
|
|
case 3: isQuestTarget = (quest as ItemDeliveryQuest).target.Value == character.Name; break;
|
|
case 4: isQuestTarget = (quest as SlayMonsterQuest).target.Value == character.Name; break;
|
|
case 7: isQuestTarget = (quest as FishingQuest).target.Value == character.Name; break;
|
|
case 10: isQuestTarget = (quest as ResourceCollectionQuest).target.Value == character.Name; break;
|
|
}
|
|
|
|
if (isQuestTarget)
|
|
Game1.spriteBatch.Draw(
|
|
Game1.mouseCursors,
|
|
new Vector2(x + 10, y - 12),
|
|
new Rectangle(394, 495, 4, 10),
|
|
Color.White,
|
|
0.0f,
|
|
Vector2.Zero,
|
|
3f,
|
|
SpriteEffects.None,
|
|
1f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ModEntry.MonitorObject.Log(ex.Message + Environment.NewLine + ex.StackTrace, LogLevel.Error);
|
|
}
|
|
}
|
|
|
|
if (namesToShow.Count > 0)
|
|
{
|
|
StringBuilder text = new StringBuilder();
|
|
int longestLength = 0;
|
|
foreach (String name in namesToShow)
|
|
{
|
|
text.AppendLine(name);
|
|
longestLength = Math.Max(longestLength, (int)Math.Ceiling(Game1.smallFont.MeasureString(name).Length()));
|
|
}
|
|
|
|
int windowHeight = Game1.smallFont.LineSpacing * namesToShow.Count + 25;
|
|
Vector2 windowPos = new Vector2(Game1.getMouseX() + 40, Game1.getMouseY() - windowHeight);
|
|
IClickableMenu.drawTextureBox(
|
|
Game1.spriteBatch,
|
|
(int)windowPos.X,
|
|
(int)windowPos.Y,
|
|
longestLength + 30,
|
|
Game1.smallFont.LineSpacing * namesToShow.Count + 25,
|
|
Color.White);
|
|
|
|
Game1.spriteBatch.DrawString(
|
|
Game1.smallFont,
|
|
text,
|
|
new Vector2(windowPos.X + 17, windowPos.Y + 17),
|
|
Game1.textShadowColor);
|
|
|
|
Game1.spriteBatch.DrawString(
|
|
Game1.smallFont,
|
|
text,
|
|
new Vector2(windowPos.X + 15, windowPos.Y + 15),
|
|
Game1.textColor);
|
|
}
|
|
|
|
//The cursor needs to show up in front of the character faces
|
|
Tools.DrawMouseCursor();
|
|
|
|
String hoverText = (String)typeof(MapPage)
|
|
.GetField(
|
|
"hoverText",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.GetValue(((List<IClickableMenu>)typeof(GameMenu)
|
|
.GetField("pages", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.GetValue(gameMenu))[gameMenu.currentTab]);
|
|
|
|
IClickableMenu.drawHoverText(
|
|
Game1.spriteBatch,
|
|
hoverText,
|
|
Game1.smallFont);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawSocialPageOptions(object sender, EventArgs e)
|
|
{
|
|
if (Game1.activeClickableMenu is GameMenu &&
|
|
(Game1.activeClickableMenu as GameMenu).currentTab == 2)
|
|
{
|
|
Game1.drawDialogueBox(
|
|
Game1.activeClickableMenu.xPositionOnScreen - SocialPanelXOffset,
|
|
Game1.activeClickableMenu.yPositionOnScreen,
|
|
SocialPanelWidth,
|
|
Game1.activeClickableMenu.height,
|
|
false,
|
|
true);
|
|
|
|
int slotPosition = (int)typeof(SocialPage)
|
|
.GetField("slotPosition", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.GetValue(_socialPage);
|
|
int yOffset = 0;
|
|
|
|
for (int i = slotPosition; i < slotPosition + 5 && i < _friendNames.Length; ++i)
|
|
{
|
|
OptionsCheckbox checkbox = _checkboxes[i];
|
|
checkbox.bounds.X = Game1.activeClickableMenu.xPositionOnScreen - 60;
|
|
|
|
checkbox.bounds.Y = Game1.activeClickableMenu.yPositionOnScreen + 130 + yOffset;
|
|
|
|
checkbox.draw(Game1.spriteBatch, 0, 0);
|
|
yOffset += 112;
|
|
Color color = checkbox.isChecked ? Color.White : Color.Gray;
|
|
|
|
Game1.spriteBatch.Draw(
|
|
Game1.mouseCursors,
|
|
new Vector2(checkbox.bounds.X - 50, checkbox.bounds.Y),
|
|
new Rectangle(80, 0, 16, 16),
|
|
color,
|
|
0.0f,
|
|
Vector2.Zero,
|
|
3f,
|
|
SpriteEffects.None,
|
|
1f);
|
|
|
|
if (yOffset != 560)
|
|
{
|
|
Game1.spriteBatch.Draw(
|
|
Game1.staminaRect,
|
|
new Rectangle(
|
|
checkbox.bounds.X - 50,
|
|
checkbox.bounds.Y + 72,
|
|
SocialPanelWidth / 2 - 6,
|
|
4),
|
|
Color.SaddleBrown);
|
|
|
|
Game1.spriteBatch.Draw(
|
|
Game1.staminaRect,
|
|
new Rectangle(
|
|
checkbox.bounds.X - 50,
|
|
checkbox.bounds.Y + 76,
|
|
SocialPanelWidth / 2 - 6,
|
|
4),
|
|
Color.BurlyWood);
|
|
}
|
|
//Game1.spriteBatch.Draw(
|
|
// Game1.mouseCursors,
|
|
// new Vector2(
|
|
// Game1.getMouseX(),
|
|
// Game1.getMouseY()),
|
|
// Game1.getSourceRectForStandardTileSheet(
|
|
// Game1.mouseCursors,
|
|
// Game1.mouseCursor,
|
|
// 16,
|
|
// 16),
|
|
// Color.White,
|
|
// 0.0f,
|
|
// Vector2.Zero,
|
|
// Game1.pixelZoom + (Game1.dialogueButtonScale / 150.0f),
|
|
// SpriteEffects.None,
|
|
// 1f);
|
|
|
|
if (checkbox.bounds.Contains(Game1.getMouseX(), Game1.getMouseY()))
|
|
IClickableMenu.drawHoverText(
|
|
Game1.spriteBatch,
|
|
"Track on map",
|
|
Game1.dialogueFont);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|