From 2a9c8d43df156ba2b6eb32c690eba4a80167a549 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 7 Jun 2017 02:08:20 -0400 Subject: add date utility --- src/StardewModdingAPI/Utilities/SDate.cs | 131 +++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/StardewModdingAPI/Utilities/SDate.cs (limited to 'src/StardewModdingAPI/Utilities') diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs new file mode 100644 index 00000000..4729bfb9 --- /dev/null +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -0,0 +1,131 @@ +using System; +using System.Linq; +using StardewValley; + +namespace StardewModdingAPI.Utilities +{ + /// Represents a Stardew Valley date. + public class SDate + { + /********* + ** Properties + *********/ + /// The internal season names in order. + private readonly string[] Seasons = { "spring", "summer", "fall", "winter" }; + + /// The number of days in a season. + private readonly int DaysInSeason = 28; + + + /********* + ** Accessors + *********/ + /// The day of month. + public int Day { get; } + + /// The season name. + public string Season { get; } + + /// The year. + public int Year { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The day of month. + /// The season name. + /// One of the arguments has an invalid value (like day 35). + public SDate(int day, string season) + : this(day, season, Game1.year) { } + + /// Construct an instance. + /// The day of month. + /// The season name. + /// The year. + /// One of the arguments has an invalid value (like day 35). + public SDate(int day, string season, int year) + { + // validate + if (season == null) + throw new ArgumentNullException(nameof(season)); + if (!this.Seasons.Contains(season)) + throw new ArgumentException($"Unknown season '{season}', must be one of [{string.Join(", ", this.Seasons)}]."); + if (day < 1 || day > this.DaysInSeason) + throw new ArgumentException($"Invalid day '{day}', must be a value from 1 to {this.DaysInSeason}."); + if (year < 1) + throw new ArgumentException($"Invalid year '{year}', must be at least 1."); + + // initialise + this.Day = day; + this.Season = season; + this.Year = year; + } + + /// Get a new date with the given number of days added. + /// The number of days to add. + /// Returns the resulting date. + /// The offset would result in an invalid date (like year 0). + public SDate AddDays(int offset) + { + // simple case + int day = this.Day + offset; + string season = this.Season; + int year = this.Year; + + // handle season transition + if (day > this.DaysInSeason || day < 1) + { + // get current season index + int curSeasonIndex = Array.IndexOf(this.Seasons, this.Season); + if (curSeasonIndex == -1) + throw new InvalidOperationException($"The current season '{this.Season}' wasn't recognised."); + + // get season offset + int seasonOffset = day / this.DaysInSeason; + if (day < 1) + seasonOffset -= 1; + + // get new date + day = this.GetWrappedIndex(day, this.DaysInSeason); + season = this.Seasons[this.GetWrappedIndex(curSeasonIndex + seasonOffset, this.Seasons.Length)]; + year += seasonOffset / this.Seasons.Length; + } + + // validate + if(year < 1) + throw new ArithmeticException($"Adding {offset} days to {this} would result in invalid date {day:00} {season} {year}."); + + // return new date + return new SDate(day, season, year); + } + + /// Get a string representation of the date. This is mainly intended for debugging or console messages. + public override string ToString() + { + return $"{this.Day:00} {this.Season} Y{this.Year}"; + } + + /// Get the current in-game date. + public static SDate Now() + { + return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year); + } + + + /********* + ** Private methods + *********/ + /// Get the real index in an array which should be treated as a two-way loop. + /// The index in the looped array. + /// The number of elements in the array. + private int GetWrappedIndex(int index, int length) + { + int wrapped = index % length; + if (wrapped < 0) + wrapped += length; + return wrapped; + } + } +} -- cgit From 230ab1738af34c89e9c308d2ce4976d4ae8dbe70 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Fri, 16 Jun 2017 03:47:36 -0700 Subject: - This adds in operators to SDate. And Tests. And a NUnit Adapter - sorry about the latter.. --- src/StardewModdingAPI.Tests/SDateTests.cs | 41 ++++++ .../StardewModdingAPI.Tests.csproj | 3 + src/StardewModdingAPI.Tests/packages.config | 1 + src/StardewModdingAPI/Utilities/SDate.cs | 150 +++++++++++++++++++++ 4 files changed, 195 insertions(+) (limited to 'src/StardewModdingAPI/Utilities') diff --git a/src/StardewModdingAPI.Tests/SDateTests.cs b/src/StardewModdingAPI.Tests/SDateTests.cs index a4c65a98..1cce6335 100644 --- a/src/StardewModdingAPI.Tests/SDateTests.cs +++ b/src/StardewModdingAPI.Tests/SDateTests.cs @@ -83,6 +83,47 @@ namespace StardewModdingAPI.Tests return this.ParseDate(dateStr).AddDays(addDays).ToString(); } + [Test(Description = "Assert that the equality operators work as expected")] + public void EqualityOperators() + { + SDate s1 = new SDate(1, "spring", 2); + SDate s2 = new SDate(1, "spring", 2); + SDate s3 = new SDate(1, "spring", 3); + SDate s4 = new SDate(12, "spring", 2); + SDate s5 = new SDate(1, "summer", 2); + + Assert.AreEqual(true, s1 == s2); + Assert.AreNotEqual(true, s1 == s3); + Assert.AreNotEqual(true, s1 == s4); + Assert.AreNotEqual(true, s1 == s5); + } + + [Test(Description = "Assert that the comparison operators work as expected")] + public void ComparisonOperators() + { + SDate s1 = new SDate(1, "spring", 2); + SDate s2 = new SDate(1, "spring", 2); + SDate s3 = new SDate(1, "spring", 3); + SDate s4 = new SDate(12, "spring", 2); + SDate s5 = new SDate(1, "summer", 2); + SDate s6 = new SDate(1, "winter", 1); + SDate s7 = new SDate(13, "fall", 1); + + Assert.AreEqual(true, s1 <= s2); + Assert.AreEqual(true, s1 >= s2); + Assert.AreEqual(true, s1 < s4); + Assert.AreEqual(true, s1 <= s4); + Assert.AreEqual(true, s4 > s1); + Assert.AreEqual(true, s4 >= s1); + Assert.AreEqual(true, s5 > s7); + Assert.AreEqual(true, s5 >= s7); + Assert.AreEqual(true, s6 < s5); + Assert.AreEqual(true, s6 <= s5); + Assert.AreEqual(true, s1 < s5); + Assert.AreEqual(true, s1 <= s5); + Assert.AreEqual(true, s5 > s1); + Assert.AreEqual(true, s5 >= s1); + } /********* ** Private methods diff --git a/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj b/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj index 3ddb1326..fbce657d 100644 --- a/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj +++ b/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj @@ -63,6 +63,9 @@ StardewModdingAPI + + + \ No newline at end of file diff --git a/src/StardewModdingAPI.Tests/packages.config b/src/StardewModdingAPI.Tests/packages.config index ba954308..d25dae06 100644 --- a/src/StardewModdingAPI.Tests/packages.config +++ b/src/StardewModdingAPI.Tests/packages.config @@ -4,4 +4,5 @@ + \ No newline at end of file diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index 4729bfb9..fdeffe80 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -113,6 +113,156 @@ namespace StardewModdingAPI.Utilities return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year); } + /********* + ** Operator methods + *********/ + + /// + /// Equality operator. Tests the date being equal to each other + /// + /// The first date being compared + /// The second date being compared + /// The equality of the dates + public static bool operator ==(SDate s1, SDate s2) + { + if (s1.Day == s2.Day && s1.Year == s2.Year && s1.Season == s2.Season) + return true; + else + return false; + } + + /// + /// Inequality operator. Tests the date being not equal to each other + /// + /// The first date being compared + /// The second date being compared + /// The inequality of the dates + public static bool operator !=(SDate s1, SDate s2) + { + if (s1.Day == s2.Day && s1.Year == s2.Year && s1.Season == s2.Season) + return false; + else + return true; + } + + /// + /// Less than operator. Tests the date being less than to each other + /// + /// The first date being compared + /// The second date being compared + /// If the dates are less than + public static bool operator >(SDate s1, SDate s2) + { + if (s1.Year > s2.Year) + return true; + else if (s1.Year == s2.Year) + { + if (s1.Season == "winter" && s2.Season != "winter") + return true; + else if (s1.Season == s2.Season && s1.Day > s2.Day) + return true; + if (s1.Season == "fall" && (s2.Season == "summer" || s2.Season == "spring")) + return true; + if (s1.Season == "summer" && s2.Season == "spring") + return true; + } + + return false; + } + + /// + /// Less or equal than operator. Tests the date being less than or equal to each other + /// + /// The first date being compared + /// The second date being compared + /// If the dates are less than or equal than + public static bool operator >=(SDate s1, SDate s2) + { + if (s1.Year > s2.Year) + return true; + else if (s1.Year == s2.Year) + { + if (s1.Season == "winter" && s2.Season != "winter") + return true; + else if (s1.Season == s2.Season && s1.Day >= s2.Day) + return true; + if (s1.Season == "fall" && (s2.Season == "summer" || s2.Season == "spring")) + return true; + if (s1.Season == "summer" && s2.Season == "spring") + return true; + } + + return false; + } + + /// + /// Greater or equal than operator. Tests the date being greater than or equal to each other + /// + /// The first date being compared + /// The second date being compared + /// If the dates are greater than or equal than + public static bool operator <=(SDate s1, SDate s2) + { + if (s1.Year < s2.Year) + return true; + else if (s1.Year == s2.Year) + { + if (s1.Season == s2.Season && s1.Day <= s2.Day) + return true; + else if (s1.Season == "spring" && s2.Season != "spring") + return true; + if (s1.Season == "summer" && (s2.Season == "fall" || s2.Season == "winter")) + return true; + if (s1.Season == "fall" && s2.Season == "winter") + return true; + } + + return false; + } + + /// + /// Greater than operator. Tests the date being greater than to each other + /// + /// The first date being compared + /// The second date being compared + /// If the dates are greater than + public static bool operator <(SDate s1, SDate s2) + { + if (s1.Year < s2.Year) + return true; + else if (s1.Year == s2.Year) + { + if (s1.Season == s2.Season && s1.Day < s2.Day) + return true; + else if (s1.Season == "spring" && s2.Season != "spring") + return true; + if (s1.Season == "summer" && (s2.Season == "fall" || s2.Season == "winter")) + return true; + if (s1.Season == "fall" && s2.Season == "winter") + return true; + } + + return false; + } + + /// + /// Overrides the equals function. + /// + /// Object being compared. + /// The equalaity of the object. + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + /// + /// This returns the hashcode of the object + /// + /// The hashcode of the object. + public override int GetHashCode() + { + return base.GetHashCode(); + } /********* ** Private methods -- cgit From 0a8c07cc0773a1e3c109a3ccfa8b95896b7d75a8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 18 Jun 2017 20:24:32 -0400 Subject: simplify date operators by making SDate.GetHashCode() return unique ordered values, expand unit tests (#307) --- src/StardewModdingAPI.Tests/SDateTests.cs | 182 +++++++++++++++++++++++------- src/StardewModdingAPI/Utilities/SDate.cs | 180 +++++++++-------------------- 2 files changed, 197 insertions(+), 165 deletions(-) (limited to 'src/StardewModdingAPI/Utilities') diff --git a/src/StardewModdingAPI.Tests/SDateTests.cs b/src/StardewModdingAPI.Tests/SDateTests.cs index 1cce6335..fa898918 100644 --- a/src/StardewModdingAPI.Tests/SDateTests.cs +++ b/src/StardewModdingAPI.Tests/SDateTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; @@ -15,10 +16,35 @@ namespace StardewModdingAPI.Tests ** Properties *********/ /// All valid seasons. - private static string[] ValidSeasons = { "spring", "summer", "fall", "winter" }; + private static readonly string[] ValidSeasons = { "spring", "summer", "fall", "winter" }; /// All valid days of a month. - private static int[] ValidDays = Enumerable.Range(1, 28).ToArray(); + private static readonly int[] ValidDays = Enumerable.Range(1, 28).ToArray(); + + /// Sample relative dates for test cases. + private static class Dates + { + /// The base date to which other dates are relative. + public const string Now = "02 summer Y2"; + + /// The day before . + public const string PrevDay = "01 summer Y2"; + + /// The month before . + public const string PrevMonth = "02 spring Y2"; + + /// The year before . + public const string PrevYear = "02 summer Y1"; + + /// The day after . + public const string NextDay = "03 summer Y2"; + + /// The month after . + public const string NextMonth = "02 fall Y2"; + + /// The year after . + public const string NextYear = "02 summer Y3"; + } /********* @@ -63,7 +89,7 @@ namespace StardewModdingAPI.Tests [TestCase("01 winter Y1", ExpectedResult = "01 winter Y1")] public string ToString(string dateStr) { - return this.ParseDate(dateStr).ToString(); + return this.GetDate(dateStr).ToString(); } /**** @@ -80,58 +106,132 @@ namespace StardewModdingAPI.Tests [TestCase("01 spring Y3", -(28 * 7 + 17), ExpectedResult = "12 spring Y1")] // negative year transition public string AddDays(string dateStr, int addDays) { - return this.ParseDate(dateStr).AddDays(addDays).ToString(); + return this.GetDate(dateStr).AddDays(addDays).ToString(); + } + + /**** + ** GetHashCode + ****/ + [Test(Description = "Assert that GetHashCode returns a unique ordered value for every date.")] + public void GetHashCode_ReturnsUniqueOrderedValue() + { + IDictionary hashes = new Dictionary(); + int lastHash = int.MinValue; + for (int year = 1; year <= 4; year++) + { + foreach (string season in SDateTests.ValidSeasons) + { + foreach (int day in SDateTests.ValidDays) + { + SDate date = new SDate(day, season, year); + int hash = date.GetHashCode(); + if (hashes.TryGetValue(hash, out SDate otherDate)) + Assert.Fail($"Received identical hash code {hash} for dates {otherDate} and {date}."); + if (hash < lastHash) + Assert.Fail($"Received smaller hash code for date {date} ({hash}) relative to {hashes[lastHash]} ({lastHash})."); + + lastHash = hash; + hashes[hash] = date; + } + } + } } - [Test(Description = "Assert that the equality operators work as expected")] - public void EqualityOperators() + [Test(Description = "Assert that the == operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)] + public bool Operators_Equals(string now, string other) { - SDate s1 = new SDate(1, "spring", 2); - SDate s2 = new SDate(1, "spring", 2); - SDate s3 = new SDate(1, "spring", 3); - SDate s4 = new SDate(12, "spring", 2); - SDate s5 = new SDate(1, "summer", 2); - - Assert.AreEqual(true, s1 == s2); - Assert.AreNotEqual(true, s1 == s3); - Assert.AreNotEqual(true, s1 == s4); - Assert.AreNotEqual(true, s1 == s5); + return this.GetDate(now) == this.GetDate(other); } - [Test(Description = "Assert that the comparison operators work as expected")] - public void ComparisonOperators() + [Test(Description = "Assert that the != operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)] + public bool Operators_NotEquals(string now, string other) { - SDate s1 = new SDate(1, "spring", 2); - SDate s2 = new SDate(1, "spring", 2); - SDate s3 = new SDate(1, "spring", 3); - SDate s4 = new SDate(12, "spring", 2); - SDate s5 = new SDate(1, "summer", 2); - SDate s6 = new SDate(1, "winter", 1); - SDate s7 = new SDate(13, "fall", 1); - - Assert.AreEqual(true, s1 <= s2); - Assert.AreEqual(true, s1 >= s2); - Assert.AreEqual(true, s1 < s4); - Assert.AreEqual(true, s1 <= s4); - Assert.AreEqual(true, s4 > s1); - Assert.AreEqual(true, s4 >= s1); - Assert.AreEqual(true, s5 > s7); - Assert.AreEqual(true, s5 >= s7); - Assert.AreEqual(true, s6 < s5); - Assert.AreEqual(true, s6 <= s5); - Assert.AreEqual(true, s1 < s5); - Assert.AreEqual(true, s1 <= s5); - Assert.AreEqual(true, s5 > s1); - Assert.AreEqual(true, s5 >= s1); + return this.GetDate(now) != this.GetDate(other); } + [Test(Description = "Assert that the < operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)] + public bool Operators_LessThan(string now, string other) + { + return this.GetDate(now) < this.GetDate(other); + } + + [Test(Description = "Assert that the <= operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)] + public bool Operators_LessThanOrEqual(string now, string other) + { + return this.GetDate(now) <= this.GetDate(other); + } + + [Test(Description = "Assert that the > operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)] + public bool Operators_MoreThan(string now, string other) + { + return this.GetDate(now) > this.GetDate(other); + } + + [Test(Description = "Assert that the > operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] + [TestCase(Dates.Now, null, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevMonth, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.PrevYear, ExpectedResult = true)] + [TestCase(Dates.Now, Dates.Now, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)] + [TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)] + public bool Operators_MoreThanOrEqual(string now, string other) + { + return this.GetDate(now) > this.GetDate(other); + } + + /********* ** Private methods *********/ /// Convert a string date into a game date, to make unit tests easier to read. /// The date string like "dd MMMM yy". - private SDate ParseDate(string dateStr) + private SDate GetDate(string dateStr) { + if (dateStr == null) + return null; + void Fail(string reason) => throw new AssertionException($"Couldn't parse date '{dateStr}' because {reason}."); // parse diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index fdeffe80..5f7ff030 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -13,6 +13,9 @@ namespace StardewModdingAPI.Utilities /// The internal season names in order. private readonly string[] Seasons = { "spring", "summer", "fall", "winter" }; + /// The number of seasons in a year. + private int SeasonsInYear => this.Seasons.Length; + /// The number of days in a season. private readonly int DaysInSeason = 28; @@ -77,10 +80,8 @@ namespace StardewModdingAPI.Utilities // handle season transition if (day > this.DaysInSeason || day < 1) { - // get current season index - int curSeasonIndex = Array.IndexOf(this.Seasons, this.Season); - if (curSeasonIndex == -1) - throw new InvalidOperationException($"The current season '{this.Season}' wasn't recognised."); + // get season index + int curSeasonIndex = this.GetSeasonIndex(); // get season offset int seasonOffset = day / this.DaysInSeason; @@ -94,7 +95,7 @@ namespace StardewModdingAPI.Utilities } // validate - if(year < 1) + if (year < 1) throw new ArithmeticException($"Adding {offset} days to {this} would result in invalid date {day:00} {season} {year}."); // return new date @@ -116,157 +117,88 @@ namespace StardewModdingAPI.Utilities /********* ** Operator methods *********/ - - /// - /// Equality operator. Tests the date being equal to each other - /// - /// The first date being compared - /// The second date being compared + /// Get whether one date is equal to another. + /// The base date to compare. + /// The other date to compare. /// The equality of the dates - public static bool operator ==(SDate s1, SDate s2) + public static bool operator ==(SDate date, SDate other) { - if (s1.Day == s2.Day && s1.Year == s2.Year && s1.Season == s2.Season) - return true; - else - return false; + return date?.GetHashCode() == other?.GetHashCode(); } - /// - /// Inequality operator. Tests the date being not equal to each other - /// - /// The first date being compared - /// The second date being compared - /// The inequality of the dates - public static bool operator !=(SDate s1, SDate s2) + /// Get whether one date is not equal to another. + /// The base date to compare. + /// The other date to compare. + public static bool operator !=(SDate date, SDate other) { - if (s1.Day == s2.Day && s1.Year == s2.Year && s1.Season == s2.Season) - return false; - else - return true; + return date?.GetHashCode() != other?.GetHashCode(); } - /// - /// Less than operator. Tests the date being less than to each other - /// - /// The first date being compared - /// The second date being compared - /// If the dates are less than - public static bool operator >(SDate s1, SDate s2) + /// Get whether one date is more than another. + /// The base date to compare. + /// The other date to compare. + public static bool operator >(SDate date, SDate other) { - if (s1.Year > s2.Year) - return true; - else if (s1.Year == s2.Year) - { - if (s1.Season == "winter" && s2.Season != "winter") - return true; - else if (s1.Season == s2.Season && s1.Day > s2.Day) - return true; - if (s1.Season == "fall" && (s2.Season == "summer" || s2.Season == "spring")) - return true; - if (s1.Season == "summer" && s2.Season == "spring") - return true; - } - - return false; + return date?.GetHashCode() > other?.GetHashCode(); } - /// - /// Less or equal than operator. Tests the date being less than or equal to each other - /// - /// The first date being compared - /// The second date being compared - /// If the dates are less than or equal than - public static bool operator >=(SDate s1, SDate s2) + /// Get whether one date is more than or equal to another. + /// The base date to compare. + /// The other date to compare. + public static bool operator >=(SDate date, SDate other) { - if (s1.Year > s2.Year) - return true; - else if (s1.Year == s2.Year) - { - if (s1.Season == "winter" && s2.Season != "winter") - return true; - else if (s1.Season == s2.Season && s1.Day >= s2.Day) - return true; - if (s1.Season == "fall" && (s2.Season == "summer" || s2.Season == "spring")) - return true; - if (s1.Season == "summer" && s2.Season == "spring") - return true; - } - - return false; + return date?.GetHashCode() >= other?.GetHashCode(); } - /// - /// Greater or equal than operator. Tests the date being greater than or equal to each other - /// - /// The first date being compared - /// The second date being compared - /// If the dates are greater than or equal than - public static bool operator <=(SDate s1, SDate s2) + /// Get whether one date is less than or equal to another. + /// The base date to compare. + /// The other date to compare. + public static bool operator <=(SDate date, SDate other) { - if (s1.Year < s2.Year) - return true; - else if (s1.Year == s2.Year) - { - if (s1.Season == s2.Season && s1.Day <= s2.Day) - return true; - else if (s1.Season == "spring" && s2.Season != "spring") - return true; - if (s1.Season == "summer" && (s2.Season == "fall" || s2.Season == "winter")) - return true; - if (s1.Season == "fall" && s2.Season == "winter") - return true; - } - - return false; + return date?.GetHashCode() <= other?.GetHashCode(); } - /// - /// Greater than operator. Tests the date being greater than to each other - /// - /// The first date being compared - /// The second date being compared - /// If the dates are greater than - public static bool operator <(SDate s1, SDate s2) + /// Get whether one date is less than another. + /// The base date to compare. + /// The other date to compare. + public static bool operator <(SDate date, SDate other) { - if (s1.Year < s2.Year) - return true; - else if (s1.Year == s2.Year) - { - if (s1.Season == s2.Season && s1.Day < s2.Day) - return true; - else if (s1.Season == "spring" && s2.Season != "spring") - return true; - if (s1.Season == "summer" && (s2.Season == "fall" || s2.Season == "winter")) - return true; - if (s1.Season == "fall" && s2.Season == "winter") - return true; - } - - return false; + return date?.GetHashCode() < other?.GetHashCode(); } - /// - /// Overrides the equals function. - /// + /// Overrides the equals function. /// Object being compared. /// The equalaity of the object. public override bool Equals(object obj) { - return base.Equals(obj); + return obj is SDate other && this == other; } - /// - /// This returns the hashcode of the object - /// - /// The hashcode of the object. + /// Get a hash code which uniquely identifies a date. public override int GetHashCode() { - return base.GetHashCode(); + // return the number of days since 01 spring Y1 + int yearIndex = this.Year - 1; + return + yearIndex * this.DaysInSeason * this.SeasonsInYear + + this.GetSeasonIndex() * this.DaysInSeason + + this.Day; } + /********* ** Private methods *********/ + /// Get the current season index. + /// The current season wasn't recognised. + private int GetSeasonIndex() + { + int index = Array.IndexOf(this.Seasons, this.Season); + if (index == -1) + throw new InvalidOperationException($"The current season '{this.Season}' wasn't recognised."); + return index; + } + /// Get the real index in an array which should be treated as a two-way loop. /// The index in the looped array. /// The number of elements in the array. -- cgit From 3e50c90230bf4f7aa4efb69b3db47dddd1e43750 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 18 Jun 2017 20:55:12 -0400 Subject: add IEquatable interface to SDate (#307) --- src/StardewModdingAPI/Utilities/SDate.cs | 61 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'src/StardewModdingAPI/Utilities') diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index 5f7ff030..e0613491 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -5,7 +5,7 @@ using StardewValley; namespace StardewModdingAPI.Utilities { /// Represents a Stardew Valley date. - public class SDate + public class SDate : IEquatable { /********* ** Properties @@ -66,6 +66,12 @@ namespace StardewModdingAPI.Utilities this.Year = year; } + /// Get the current in-game date. + public static SDate Now() + { + return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year); + } + /// Get a new date with the given number of days added. /// The number of days to add. /// Returns the resulting date. @@ -108,15 +114,37 @@ namespace StardewModdingAPI.Utilities return $"{this.Day:00} {this.Season} Y{this.Year}"; } - /// Get the current in-game date. - public static SDate Now() + /**** + ** IEquatable + ****/ + /// Get whether this instance is equal to another. + /// The other value to compare. + public bool Equals(SDate other) { - return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year); + return this == other; } - /********* - ** Operator methods - *********/ + /// Get whether this instance is equal to another. + /// The other value to compare. + public override bool Equals(object obj) + { + return obj is SDate other && this == other; + } + + /// Get a hash code which uniquely identifies a date. + public override int GetHashCode() + { + // return the number of days since 01 spring Y1 + int yearIndex = this.Year - 1; + return + yearIndex * this.DaysInSeason * this.SeasonsInYear + + this.GetSeasonIndex() * this.DaysInSeason + + this.Day; + } + + /**** + ** Operators + ****/ /// Get whether one date is equal to another. /// The base date to compare. /// The other date to compare. @@ -166,25 +194,6 @@ namespace StardewModdingAPI.Utilities return date?.GetHashCode() < other?.GetHashCode(); } - /// Overrides the equals function. - /// Object being compared. - /// The equalaity of the object. - public override bool Equals(object obj) - { - return obj is SDate other && this == other; - } - - /// Get a hash code which uniquely identifies a date. - public override int GetHashCode() - { - // return the number of days since 01 spring Y1 - int yearIndex = this.Year - 1; - return - yearIndex * this.DaysInSeason * this.SeasonsInYear - + this.GetSeasonIndex() * this.DaysInSeason - + this.Day; - } - /********* ** Private methods -- cgit From 8d301162d87558826ed8fc8f2352800bf674ddf0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 5 Jul 2017 15:41:58 -0400 Subject: add InputEvents which unify keyboard, mouse, and controller input with more metadata (#316) --- release-notes.md | 6 +- src/StardewModdingAPI/Events/EventArgsInput.cs | 38 ++ src/StardewModdingAPI/Events/InputEvents.cs | 45 ++ src/StardewModdingAPI/Framework/CursorPosition.cs | 37 ++ src/StardewModdingAPI/Framework/SGame.cs | 268 ++++----- src/StardewModdingAPI/ICursorPosition.cs | 19 + src/StardewModdingAPI/StardewModdingAPI.csproj | 5 + src/StardewModdingAPI/Utilities/SButton.cs | 659 ++++++++++++++++++++++ 8 files changed, 932 insertions(+), 145 deletions(-) create mode 100644 src/StardewModdingAPI/Events/EventArgsInput.cs create mode 100644 src/StardewModdingAPI/Events/InputEvents.cs create mode 100644 src/StardewModdingAPI/Framework/CursorPosition.cs create mode 100644 src/StardewModdingAPI/ICursorPosition.cs create mode 100644 src/StardewModdingAPI/Utilities/SButton.cs (limited to 'src/StardewModdingAPI/Utilities') diff --git a/release-notes.md b/release-notes.md index 2283d6d1..5d906f6c 100644 --- a/release-notes.md +++ b/release-notes.md @@ -6,8 +6,10 @@ For players: * The SMAPI console is now much simpler and easier-to-read. For mod developers: -* SMAPI mods can now edit XNB images & data loaded by the game (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Content)). -* SMAPI mods can now inject new XNB images & data (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Content)). +* Added API to edit XNB images & data loaded by the game (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Content)). +* Added API to inject new XNB images & data (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Content)). +* Added `InputEvents` which unify keyboard, mouse, and controller input for much simpler input handling (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Input_events)). +* Added useful `InputEvents` metadata like the cursor position, grab tile, etc. * The `manifest.json` version can now be specified as a string. ## 1.15 diff --git a/src/StardewModdingAPI/Events/EventArgsInput.cs b/src/StardewModdingAPI/Events/EventArgsInput.cs new file mode 100644 index 00000000..1d5e6fde --- /dev/null +++ b/src/StardewModdingAPI/Events/EventArgsInput.cs @@ -0,0 +1,38 @@ +#if SMAPI_2_0 +using System; +using StardewModdingAPI.Utilities; + +namespace StardewModdingAPI.Events +{ + /// Event arguments when a button is pressed or released. + public class EventArgsInput : EventArgs + { + /********* + ** Accessors + *********/ + /// The button on the controller, keyboard, or mouse. + public SButton Button { get; } + + /// The current cursor position. + public ICursorPosition Cursor { get; set; } + + /// Whether the input is considered a 'click' by the game for enabling action. + public bool IsClick { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The button on the controller, keyboard, or mouse. + /// The cursor position. + /// Whether the input is considered a 'click' by the game for enabling action. + public EventArgsInput(SButton button, ICursorPosition cursor, bool isClick) + { + this.Button = button; + this.Cursor = cursor; + this.IsClick = isClick; + } + } +} +#endif diff --git a/src/StardewModdingAPI/Events/InputEvents.cs b/src/StardewModdingAPI/Events/InputEvents.cs new file mode 100644 index 00000000..285487af --- /dev/null +++ b/src/StardewModdingAPI/Events/InputEvents.cs @@ -0,0 +1,45 @@ +#if SMAPI_2_0 +using System; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Utilities; + +namespace StardewModdingAPI.Events +{ + /// Events raised when the player uses a controller, keyboard, or mouse button. + public static class InputEvents + { + /********* + ** Events + *********/ + /// Raised when the player presses a button on the keyboard, controller, or mouse. + public static event EventHandler ButtonPressed; + + /// Raised when the player releases a keyboard key on the keyboard, controller, or mouse. + public static event EventHandler ButtonReleased; + + + /********* + ** Internal methods + *********/ + /// Raise a event. + /// Encapsulates monitoring and logging. + /// The button on the controller, keyboard, or mouse. + /// The cursor position. + /// Whether the input is considered a 'click' by the game for enabling action. + internal static void InvokeButtonPressed(IMonitor monitor, SButton button, ICursorPosition cursor, bool isClick) + { + monitor.SafelyRaiseGenericEvent($"{nameof(InputEvents)}.{nameof(InputEvents.ButtonPressed)}", InputEvents.ButtonPressed?.GetInvocationList(), null, new EventArgsInput(button, cursor, isClick)); + } + + /// Raise a event. + /// Encapsulates monitoring and logging. + /// The button on the controller, keyboard, or mouse. + /// The cursor position. + /// Whether the input is considered a 'click' by the game for enabling action. + internal static void InvokeButtonReleased(IMonitor monitor, SButton button, ICursorPosition cursor, bool isClick) + { + monitor.SafelyRaiseGenericEvent($"{nameof(InputEvents)}.{nameof(InputEvents.ButtonReleased)}", InputEvents.ButtonReleased?.GetInvocationList(), null, new EventArgsInput(button, cursor, isClick)); + } + } +} +#endif \ No newline at end of file diff --git a/src/StardewModdingAPI/Framework/CursorPosition.cs b/src/StardewModdingAPI/Framework/CursorPosition.cs new file mode 100644 index 00000000..4f256da5 --- /dev/null +++ b/src/StardewModdingAPI/Framework/CursorPosition.cs @@ -0,0 +1,37 @@ +#if SMAPI_2_0 +using Microsoft.Xna.Framework; + +namespace StardewModdingAPI.Framework +{ + /// Defines a position on a given map at different reference points. + internal class CursorPosition : ICursorPosition + { + /********* + ** Accessors + *********/ + /// The pixel position relative to the top-left corner of the visible screen. + public Vector2 ScreenPixels { get; } + + /// The tile position under the cursor relative to the top-left corner of the map. + public Vector2 Tile { get; } + + /// The tile position that the game considers under the cursor for purposes of clicking actions. This may be different than if that's too far from the player. + public Vector2 GrabTile { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The pixel position relative to the top-left corner of the visible screen. + /// The tile position relative to the top-left corner of the map. + /// The tile position that the game considers under the cursor for purposes of clicking actions. + public CursorPosition(Vector2 screenPixels, Vector2 tile, Vector2 grabTile) + { + this.ScreenPixels = screenPixels; + this.Tile = tile; + this.GrabTile = grabTile; + } + } +} +#endif diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index 678dcf3a..f2c5c0c9 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -10,6 +10,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using StardewModdingAPI.Events; +using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.BellsAndWhistles; using StardewValley.Locations; @@ -59,12 +60,15 @@ namespace StardewModdingAPI.Framework /**** ** Game state ****/ - /// Arrays of pressed controller buttons indexed by . - private Buttons[] PreviousPressedButtons = new Buttons[0]; + /// A record of the buttons pressed as of the previous tick. + private SButton[] PreviousPressedButtons = new SButton[0]; /// A record of the keyboard state (i.e. the up/down state for each button) as of the previous tick. private KeyboardState PreviousKeyState; + /// A record of the controller state (i.e. the up/down state for each button) as of the previous tick. + private GamePadState PreviousControllerState; + /// A record of the mouse state (i.e. the cursor position, scroll amount, and the up/down state for each button) as of the previous tick. private MouseState PreviousMouseState; @@ -351,64 +355,95 @@ namespace StardewModdingAPI.Framework { // get latest state KeyboardState keyState; + GamePadState controllerState; MouseState mouseState; Point mousePosition; try { keyState = Keyboard.GetState(); + controllerState = GamePad.GetState(PlayerIndex.One); mouseState = Mouse.GetState(); mousePosition = new Point(Game1.getMouseX(), Game1.getMouseY()); } catch (InvalidOperationException) // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true { keyState = this.PreviousKeyState; + controllerState = this.PreviousControllerState; mouseState = this.PreviousMouseState; mousePosition = this.PreviousMousePosition; } // analyse state - Keys[] currentlyPressedKeys = keyState.GetPressedKeys(); - Keys[] previousPressedKeys = this.PreviousKeyState.GetPressedKeys(); - Keys[] framePressedKeys = currentlyPressedKeys.Except(previousPressedKeys).ToArray(); - Keys[] frameReleasedKeys = previousPressedKeys.Except(currentlyPressedKeys).ToArray(); - - // raise key pressed - foreach (Keys key in framePressedKeys) - ControlEvents.InvokeKeyPressed(this.Monitor, key); - - // raise key released - foreach (Keys key in frameReleasedKeys) - ControlEvents.InvokeKeyReleased(this.Monitor, key); + SButton[] currentlyPressedKeys = this.GetPressedButtons(keyState, mouseState, controllerState).ToArray(); + SButton[] previousPressedKeys = this.PreviousPressedButtons; + SButton[] framePressedKeys = currentlyPressedKeys.Except(previousPressedKeys).ToArray(); + SButton[] frameReleasedKeys = previousPressedKeys.Except(currentlyPressedKeys).ToArray(); + bool isClick = framePressedKeys.Contains(SButton.MouseLeft) || (framePressedKeys.Contains(SButton.ControllerA) && !currentlyPressedKeys.Contains(SButton.ControllerX)); + + // get cursor position +#if SMAPI_2_0 + ICursorPosition cursor; + { + // cursor position + Vector2 screenPixels = new Vector2(Game1.getMouseX(), Game1.getMouseY()); + Vector2 tile = new Vector2((Game1.viewport.X + screenPixels.X) / Game1.tileSize, (Game1.viewport.Y + screenPixels.Y) / Game1.tileSize); + Vector2 grabTile = (Game1.mouseCursorTransparency > 0 && Utility.tileWithinRadiusOfPlayer((int)tile.X, (int)tile.Y, 1, Game1.player)) // derived from Game1.pressActionButton + ? tile + : Game1.player.GetGrabTile(); + cursor = new CursorPosition(screenPixels, tile, grabTile); + } +#endif - // raise controller button pressed - foreach (Buttons button in this.GetFramePressedButtons()) + // raise button pressed + foreach (SButton button in framePressedKeys) { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) +#if SMAPI_2_0 + InputEvents.InvokeButtonPressed(this.Monitor, button, cursor, isClick); +#endif + + // legacy events + if (button.TryGetKeyboard(out Keys key)) { - var triggers = GamePad.GetState(PlayerIndex.One).Triggers; - ControlEvents.InvokeTriggerPressed(this.Monitor, button, button == Buttons.LeftTrigger ? triggers.Left : triggers.Right); + if (key != Keys.None) + ControlEvents.InvokeKeyPressed(this.Monitor, key); + } + else if (button.TryGetController(out Buttons controllerButton)) + { + if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) + ControlEvents.InvokeTriggerPressed(this.Monitor, controllerButton, controllerButton == Buttons.LeftTrigger ? controllerState.Triggers.Left : controllerState.Triggers.Right); + else + ControlEvents.InvokeButtonPressed(this.Monitor, controllerButton); } - else - ControlEvents.InvokeButtonPressed(this.Monitor, button); } - // raise controller button released - foreach (Buttons button in this.GetFrameReleasedButtons()) + // raise button released + foreach (SButton button in frameReleasedKeys) { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) +#if SMAPI_2_0 + bool wasClick = + (button == SButton.MouseLeft && previousPressedKeys.Contains(SButton.MouseLeft)) // released left click + || (button == SButton.ControllerA && previousPressedKeys.Contains(SButton.ControllerA) && !previousPressedKeys.Contains(SButton.ControllerX)); + InputEvents.InvokeButtonReleased(this.Monitor, button, cursor, wasClick); +#endif + + // legacy events + if (button.TryGetKeyboard(out Keys key)) { - var triggers = GamePad.GetState(PlayerIndex.One).Triggers; - ControlEvents.InvokeTriggerReleased(this.Monitor, button, button == Buttons.LeftTrigger ? triggers.Left : triggers.Right); + if (key != Keys.None) + ControlEvents.InvokeKeyReleased(this.Monitor, key); + } + else if (button.TryGetController(out Buttons controllerButton)) + { + if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) + ControlEvents.InvokeTriggerReleased(this.Monitor, controllerButton, controllerButton == Buttons.LeftTrigger ? controllerState.Triggers.Left : controllerState.Triggers.Right); + else + ControlEvents.InvokeButtonReleased(this.Monitor, controllerButton); } - else - ControlEvents.InvokeButtonReleased(this.Monitor, button); } - // raise keyboard state changed + // raise legacy state-changed events if (keyState != this.PreviousKeyState) ControlEvents.InvokeKeyboardChanged(this.Monitor, this.PreviousKeyState, keyState); - - // raise mouse state changed if (mouseState != this.PreviousMouseState) ControlEvents.InvokeMouseChanged(this.Monitor, this.PreviousMouseState, mouseState, this.PreviousMousePosition, mousePosition); @@ -416,7 +451,8 @@ namespace StardewModdingAPI.Framework this.PreviousMouseState = mouseState; this.PreviousMousePosition = mousePosition; this.PreviousKeyState = keyState; - this.PreviousPressedButtons = this.GetButtonsDown(); + this.PreviousControllerState = controllerState; + this.PreviousPressedButtons = currentlyPressedKeys; } /********* @@ -1308,120 +1344,66 @@ namespace StardewModdingAPI.Framework this.PreviousSaveID = 0; } - /// Get the controller buttons which are currently pressed. - private Buttons[] GetButtonsDown() + /// Get the buttons pressed in the given stats. + /// The keyboard state. + /// The mouse state. + /// The controller state. + private IEnumerable GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) { - var state = GamePad.GetState(PlayerIndex.One); - var buttons = new List(); - if (state.IsConnected) + // keyboard + foreach (Keys key in keyboard.GetPressedKeys()) + yield return key.ToSButton(); + + // mouse + if (mouse.LeftButton == ButtonState.Pressed) + yield return SButton.MouseLeft; + if (mouse.RightButton == ButtonState.Pressed) + yield return SButton.MouseRight; + if (mouse.MiddleButton == ButtonState.Pressed) + yield return SButton.MouseMiddle; + if (mouse.XButton1 == ButtonState.Pressed) + yield return SButton.MouseX1; + if (mouse.XButton2 == ButtonState.Pressed) + yield return SButton.MouseX2; + + // controller + if (controller.IsConnected) { - if (state.Buttons.A == ButtonState.Pressed) buttons.Add(Buttons.A); - if (state.Buttons.B == ButtonState.Pressed) buttons.Add(Buttons.B); - if (state.Buttons.Back == ButtonState.Pressed) buttons.Add(Buttons.Back); - if (state.Buttons.BigButton == ButtonState.Pressed) buttons.Add(Buttons.BigButton); - if (state.Buttons.LeftShoulder == ButtonState.Pressed) buttons.Add(Buttons.LeftShoulder); - if (state.Buttons.LeftStick == ButtonState.Pressed) buttons.Add(Buttons.LeftStick); - if (state.Buttons.RightShoulder == ButtonState.Pressed) buttons.Add(Buttons.RightShoulder); - if (state.Buttons.RightStick == ButtonState.Pressed) buttons.Add(Buttons.RightStick); - if (state.Buttons.Start == ButtonState.Pressed) buttons.Add(Buttons.Start); - if (state.Buttons.X == ButtonState.Pressed) buttons.Add(Buttons.X); - if (state.Buttons.Y == ButtonState.Pressed) buttons.Add(Buttons.Y); - if (state.DPad.Up == ButtonState.Pressed) buttons.Add(Buttons.DPadUp); - if (state.DPad.Down == ButtonState.Pressed) buttons.Add(Buttons.DPadDown); - if (state.DPad.Left == ButtonState.Pressed) buttons.Add(Buttons.DPadLeft); - if (state.DPad.Right == ButtonState.Pressed) buttons.Add(Buttons.DPadRight); - if (state.Triggers.Left > 0.2f) buttons.Add(Buttons.LeftTrigger); - if (state.Triggers.Right > 0.2f) buttons.Add(Buttons.RightTrigger); + if (controller.Buttons.A == ButtonState.Pressed) + yield return SButton.ControllerA; + if (controller.Buttons.B == ButtonState.Pressed) + yield return SButton.ControllerB; + if (controller.Buttons.Back == ButtonState.Pressed) + yield return SButton.ControllerBack; + if (controller.Buttons.BigButton == ButtonState.Pressed) + yield return SButton.BigButton; + if (controller.Buttons.LeftShoulder == ButtonState.Pressed) + yield return SButton.LeftShoulder; + if (controller.Buttons.LeftStick == ButtonState.Pressed) + yield return SButton.LeftStick; + if (controller.Buttons.RightShoulder == ButtonState.Pressed) + yield return SButton.RightShoulder; + if (controller.Buttons.RightStick == ButtonState.Pressed) + yield return SButton.RightStick; + if (controller.Buttons.Start == ButtonState.Pressed) + yield return SButton.ControllerStart; + if (controller.Buttons.X == ButtonState.Pressed) + yield return SButton.ControllerX; + if (controller.Buttons.Y == ButtonState.Pressed) + yield return SButton.ControllerY; + if (controller.DPad.Up == ButtonState.Pressed) + yield return SButton.DPadUp; + if (controller.DPad.Down == ButtonState.Pressed) + yield return SButton.DPadDown; + if (controller.DPad.Left == ButtonState.Pressed) + yield return SButton.DPadLeft; + if (controller.DPad.Right == ButtonState.Pressed) + yield return SButton.DPadRight; + if (controller.Triggers.Left > 0.2f) + yield return SButton.LeftTrigger; + if (controller.Triggers.Right > 0.2f) + yield return SButton.RightTrigger; } - return buttons.ToArray(); - } - - /// Get the controller buttons which were pressed after the last update. - private Buttons[] GetFramePressedButtons() - { - var state = GamePad.GetState(PlayerIndex.One); - var buttons = new List(); - if (state.IsConnected) - { - if (this.WasButtonJustPressed(Buttons.A, state.Buttons.A)) buttons.Add(Buttons.A); - if (this.WasButtonJustPressed(Buttons.B, state.Buttons.B)) buttons.Add(Buttons.B); - if (this.WasButtonJustPressed(Buttons.Back, state.Buttons.Back)) buttons.Add(Buttons.Back); - if (this.WasButtonJustPressed(Buttons.BigButton, state.Buttons.BigButton)) buttons.Add(Buttons.BigButton); - if (this.WasButtonJustPressed(Buttons.LeftShoulder, state.Buttons.LeftShoulder)) buttons.Add(Buttons.LeftShoulder); - if (this.WasButtonJustPressed(Buttons.LeftStick, state.Buttons.LeftStick)) buttons.Add(Buttons.LeftStick); - if (this.WasButtonJustPressed(Buttons.RightShoulder, state.Buttons.RightShoulder)) buttons.Add(Buttons.RightShoulder); - if (this.WasButtonJustPressed(Buttons.RightStick, state.Buttons.RightStick)) buttons.Add(Buttons.RightStick); - if (this.WasButtonJustPressed(Buttons.Start, state.Buttons.Start)) buttons.Add(Buttons.Start); - if (this.WasButtonJustPressed(Buttons.X, state.Buttons.X)) buttons.Add(Buttons.X); - if (this.WasButtonJustPressed(Buttons.Y, state.Buttons.Y)) buttons.Add(Buttons.Y); - if (this.WasButtonJustPressed(Buttons.DPadUp, state.DPad.Up)) buttons.Add(Buttons.DPadUp); - if (this.WasButtonJustPressed(Buttons.DPadDown, state.DPad.Down)) buttons.Add(Buttons.DPadDown); - if (this.WasButtonJustPressed(Buttons.DPadLeft, state.DPad.Left)) buttons.Add(Buttons.DPadLeft); - if (this.WasButtonJustPressed(Buttons.DPadRight, state.DPad.Right)) buttons.Add(Buttons.DPadRight); - if (this.WasButtonJustPressed(Buttons.LeftTrigger, state.Triggers.Left)) buttons.Add(Buttons.LeftTrigger); - if (this.WasButtonJustPressed(Buttons.RightTrigger, state.Triggers.Right)) buttons.Add(Buttons.RightTrigger); - } - return buttons.ToArray(); - } - - /// Get the controller buttons which were released after the last update. - private Buttons[] GetFrameReleasedButtons() - { - var state = GamePad.GetState(PlayerIndex.One); - var buttons = new List(); - if (state.IsConnected) - { - if (this.WasButtonJustReleased(Buttons.A, state.Buttons.A)) buttons.Add(Buttons.A); - if (this.WasButtonJustReleased(Buttons.B, state.Buttons.B)) buttons.Add(Buttons.B); - if (this.WasButtonJustReleased(Buttons.Back, state.Buttons.Back)) buttons.Add(Buttons.Back); - if (this.WasButtonJustReleased(Buttons.BigButton, state.Buttons.BigButton)) buttons.Add(Buttons.BigButton); - if (this.WasButtonJustReleased(Buttons.LeftShoulder, state.Buttons.LeftShoulder)) buttons.Add(Buttons.LeftShoulder); - if (this.WasButtonJustReleased(Buttons.LeftStick, state.Buttons.LeftStick)) buttons.Add(Buttons.LeftStick); - if (this.WasButtonJustReleased(Buttons.RightShoulder, state.Buttons.RightShoulder)) buttons.Add(Buttons.RightShoulder); - if (this.WasButtonJustReleased(Buttons.RightStick, state.Buttons.RightStick)) buttons.Add(Buttons.RightStick); - if (this.WasButtonJustReleased(Buttons.Start, state.Buttons.Start)) buttons.Add(Buttons.Start); - if (this.WasButtonJustReleased(Buttons.X, state.Buttons.X)) buttons.Add(Buttons.X); - if (this.WasButtonJustReleased(Buttons.Y, state.Buttons.Y)) buttons.Add(Buttons.Y); - if (this.WasButtonJustReleased(Buttons.DPadUp, state.DPad.Up)) buttons.Add(Buttons.DPadUp); - if (this.WasButtonJustReleased(Buttons.DPadDown, state.DPad.Down)) buttons.Add(Buttons.DPadDown); - if (this.WasButtonJustReleased(Buttons.DPadLeft, state.DPad.Left)) buttons.Add(Buttons.DPadLeft); - if (this.WasButtonJustReleased(Buttons.DPadRight, state.DPad.Right)) buttons.Add(Buttons.DPadRight); - if (this.WasButtonJustReleased(Buttons.LeftTrigger, state.Triggers.Left)) buttons.Add(Buttons.LeftTrigger); - if (this.WasButtonJustReleased(Buttons.RightTrigger, state.Triggers.Right)) buttons.Add(Buttons.RightTrigger); - } - return buttons.ToArray(); - } - - /// Get whether a controller button was pressed since the last check. - /// The controller button to check. - /// The last known state. - private bool WasButtonJustPressed(Buttons button, ButtonState buttonState) - { - return buttonState == ButtonState.Pressed && !this.PreviousPressedButtons.Contains(button); - } - - /// Get whether a controller button was released since the last check. - /// The controller button to check. - /// The last known state. - private bool WasButtonJustReleased(Buttons button, ButtonState buttonState) - { - return buttonState == ButtonState.Released && this.PreviousPressedButtons.Contains(button); - } - - /// Get whether an analogue controller button was pressed since the last check. - /// The controller button to check. - /// The last known value. - private bool WasButtonJustPressed(Buttons button, float value) - { - return this.WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released); - } - - /// Get whether an analogue controller button was released since the last check. - /// The controller button to check. - /// The last known value. - private bool WasButtonJustReleased(Buttons button, float value) - { - return this.WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released); } /// Get the player inventory changes between two states. diff --git a/src/StardewModdingAPI/ICursorPosition.cs b/src/StardewModdingAPI/ICursorPosition.cs new file mode 100644 index 00000000..d03cda71 --- /dev/null +++ b/src/StardewModdingAPI/ICursorPosition.cs @@ -0,0 +1,19 @@ +#if SMAPI_2_0 +using Microsoft.Xna.Framework; + +namespace StardewModdingAPI +{ + /// Represents a cursor position in the different coordinate systems. + public interface ICursorPosition + { + /// The pixel position relative to the top-left corner of the visible screen. + Vector2 ScreenPixels { get; } + + /// The tile position under the cursor relative to the top-left corner of the map. + Vector2 Tile { get; } + + /// The tile position that the game considers under the cursor for purposes of clicking actions. This may be different than if that's too far from the player. + Vector2 GrabTile { get; } + } +} +#endif diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index bf1c43d1..c442cc8a 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -93,7 +93,9 @@ + + @@ -207,7 +209,10 @@ + + + diff --git a/src/StardewModdingAPI/Utilities/SButton.cs b/src/StardewModdingAPI/Utilities/SButton.cs new file mode 100644 index 00000000..f4fccfff --- /dev/null +++ b/src/StardewModdingAPI/Utilities/SButton.cs @@ -0,0 +1,659 @@ +using System; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI.Utilities +{ + /// A unified button constant which includes all controller, keyboard, and mouse buttons. + /// Derived from , , and . +#if SMAPI_2_0 + public +#else + internal +#endif + enum SButton + { + /// No valid key. + None = 0, + + /********* + ** Mouse + *********/ + /// The left mouse button. + MouseLeft = 1000, + + /// The right mouse button. + MouseRight = 1001, + + /// The middle mouse button. + MouseMiddle = 1002, + + /// The first mouse XButton. + MouseX1 = 1003, + + /// The second mouse XButton. + MouseX2 = 1004, + + /********* + ** Controller + *********/ + /// The 'A' button on a controller. + ControllerA = SButtonExtensions.ControllerOffset + Buttons.A, + + /// The 'B' button on a controller. + ControllerB = SButtonExtensions.ControllerOffset + Buttons.B, + + /// The 'X' button on a controller. + ControllerX = SButtonExtensions.ControllerOffset + Buttons.X, + + /// The 'Y' button on a controller. + ControllerY = SButtonExtensions.ControllerOffset + Buttons.Y, + + /// The back button on a controller. + ControllerBack = SButtonExtensions.ControllerOffset + Buttons.Back, + + /// The start button on a controller. + ControllerStart = SButtonExtensions.ControllerOffset + Buttons.Start, + + /// The up button on the directional pad of a controller. + DPadUp = SButtonExtensions.ControllerOffset + Buttons.DPadUp, + + /// The down button on the directional pad of a controller. + DPadDown = SButtonExtensions.ControllerOffset + Buttons.DPadDown, + + /// The left button on the directional pad of a controller. + DPadLeft = SButtonExtensions.ControllerOffset + Buttons.DPadLeft, + + /// The right button on the directional pad of a controller. + DPadRight = SButtonExtensions.ControllerOffset + Buttons.DPadRight, + + /// The left bumper (shoulder) button on a controller. + LeftShoulder = SButtonExtensions.ControllerOffset + Buttons.LeftShoulder, + + /// The right bumper (shoulder) button on a controller. + RightShoulder = SButtonExtensions.ControllerOffset + Buttons.RightShoulder, + + /// The left trigger on a controller. + LeftTrigger = SButtonExtensions.ControllerOffset + Buttons.LeftTrigger, + + /// The right trigger on a controller. + RightTrigger = SButtonExtensions.ControllerOffset + Buttons.RightTrigger, + + /// The left analog stick on a controller (when pressed). + LeftStick = SButtonExtensions.ControllerOffset + Buttons.LeftStick, + + /// The right analog stick on a controller (when pressed). + RightStick = SButtonExtensions.ControllerOffset + Buttons.RightStick, + + /// The 'big button' on a controller. + BigButton = SButtonExtensions.ControllerOffset + Buttons.BigButton, + + /// The left analog stick on a controller (when pushed left). + LeftThumbstickLeft = SButtonExtensions.ControllerOffset + Buttons.LeftThumbstickLeft, + + /// The left analog stick on a controller (when pushed right). + LeftThumbstickRight = SButtonExtensions.ControllerOffset + Buttons.LeftThumbstickRight, + + /// The left analog stick on a controller (when pushed down). + LeftThumbstickDown = SButtonExtensions.ControllerOffset + Buttons.LeftThumbstickDown, + + /// The left analog stick on a controller (when pushed up). + LeftThumbstickUp = SButtonExtensions.ControllerOffset + Buttons.LeftThumbstickUp, + + /// The right analog stick on a controller (when pushed left). + RightThumbstickLeft = SButtonExtensions.ControllerOffset + Buttons.RightThumbstickLeft, + + /// The right analog stick on a controller (when pushed right). + RightThumbstickRight = SButtonExtensions.ControllerOffset + Buttons.RightThumbstickRight, + + /// The right analog stick on a controller (when pushed down). + RightThumbstickDown = SButtonExtensions.ControllerOffset + Buttons.RightThumbstickDown, + + /// The right analog stick on a controller (when pushed up). + RightThumbstickUp = SButtonExtensions.ControllerOffset + Buttons.RightThumbstickUp, + + /********* + ** Keyboard + *********/ + /// The A button on a keyboard. + A = Keys.A, + + /// The Add button on a keyboard. + Add = Keys.Add, + + /// The Applications button on a keyboard. + Apps = Keys.Apps, + + /// The Attn button on a keyboard. + Attn = Keys.Attn, + + /// The B button on a keyboard. + B = Keys.B, + + /// The Backspace button on a keyboard. + Back = Keys.Back, + + /// The Browser Back button on a keyboard in Windows 2000/XP. + BrowserBack = Keys.BrowserBack, + + /// The Browser Favorites button on a keyboard in Windows 2000/XP. + BrowserFavorites = Keys.BrowserFavorites, + + /// The Browser Favorites button on a keyboard in Windows 2000/XP. + BrowserForward = Keys.BrowserForward, + + /// The Browser Home button on a keyboard in Windows 2000/XP. + BrowserHome = Keys.BrowserHome, + + /// The Browser Refresh button on a keyboard in Windows 2000/XP. + BrowserRefresh = Keys.BrowserRefresh, + + /// The Browser Search button on a keyboard in Windows 2000/XP. + BrowserSearch = Keys.BrowserSearch, + + /// The Browser Stop button on a keyboard in Windows 2000/XP. + BrowserStop = Keys.BrowserStop, + + /// The C button on a keyboard. + C = Keys.C, + + /// The Caps Lock button on a keyboard. + CapsLock = Keys.CapsLock, + + /// The Green ChatPad button on a keyboard. + ChatPadGreen = Keys.ChatPadGreen, + + /// The Orange ChatPad button on a keyboard. + ChatPadOrange = Keys.ChatPadOrange, + + /// The CrSel button on a keyboard. + Crsel = Keys.Crsel, + + /// The D button on a keyboard. + D = Keys.D, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D0 = Keys.D0, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D1 = Keys.D1, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D2 = Keys.D2, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D3 = Keys.D3, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D4 = Keys.D4, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D5 = Keys.D5, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D6 = Keys.D6, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D7 = Keys.D7, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D8 = Keys.D8, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + D9 = Keys.D9, + + /// The Decimal button on a keyboard. + Decimal = Keys.Decimal, + + /// The Delete button on a keyboard. + Delete = Keys.Delete, + + /// The Divide button on a keyboard. + Divide = Keys.Divide, + + /// The Down arrow button on a keyboard. + Down = Keys.Down, + + /// The E button on a keyboard. + E = Keys.E, + + /// The End button on a keyboard. + End = Keys.End, + + /// The Enter button on a keyboard. + Enter = Keys.Enter, + + /// The Erase EOF button on a keyboard. + EraseEof = Keys.EraseEof, + + /// The Escape button on a keyboard. + Escape = Keys.Escape, + + /// The Execute button on a keyboard. + Execute = Keys.Execute, + + /// The ExSel button on a keyboard. + Exsel = Keys.Exsel, + + /// The F button on a keyboard. + F = Keys.F, + + /// The F1 button on a keyboard. + F1 = Keys.F1, + + /// The F10 button on a keyboard. + F10 = Keys.F10, + + /// The F11 button on a keyboard. + F11 = Keys.F11, + + /// The F12 button on a keyboard. + F12 = Keys.F12, + + /// The F13 button on a keyboard. + F13 = Keys.F13, + + /// The F14 button on a keyboard. + F14 = Keys.F14, + + /// The F15 button on a keyboard. + F15 = Keys.F15, + + /// The F16 button on a keyboard. + F16 = Keys.F16, + + /// The F17 button on a keyboard. + F17 = Keys.F17, + + /// The F18 button on a keyboard. + F18 = Keys.F18, + + /// The F19 button on a keyboard. + F19 = Keys.F19, + + /// The F2 button on a keyboard. + F2 = Keys.F2, + + /// The F20 button on a keyboard. + F20 = Keys.F20, + + /// The F21 button on a keyboard. + F21 = Keys.F21, + + /// The F22 button on a keyboard. + F22 = Keys.F22, + + /// The F23 button on a keyboard. + F23 = Keys.F23, + + /// The F24 button on a keyboard. + F24 = Keys.F24, + + /// The F3 button on a keyboard. + F3 = Keys.F3, + + /// The F4 button on a keyboard. + F4 = Keys.F4, + + /// The F5 button on a keyboard. + F5 = Keys.F5, + + /// The F6 button on a keyboard. + F6 = Keys.F6, + + /// The F7 button on a keyboard. + F7 = Keys.F7, + + /// The F8 button on a keyboard. + F8 = Keys.F8, + + /// The F9 button on a keyboard. + F9 = Keys.F9, + + /// The G button on a keyboard. + G = Keys.G, + + /// The H button on a keyboard. + H = Keys.H, + + /// The Help button on a keyboard. + Help = Keys.Help, + + /// The Home button on a keyboard. + Home = Keys.Home, + + /// The I button on a keyboard. + I = Keys.I, + + /// The IME Convert button on a keyboard. + ImeConvert = Keys.ImeConvert, + + /// The IME NoConvert button on a keyboard. + ImeNoConvert = Keys.ImeNoConvert, + + /// The INS button on a keyboard. + Insert = Keys.Insert, + + /// The J button on a keyboard. + J = Keys.J, + + /// The K button on a keyboard. + K = Keys.K, + + /// The Kana button on a Japanese keyboard. + Kana = Keys.Kana, + + /// The Kanji button on a Japanese keyboard. + Kanji = Keys.Kanji, + + /// The L button on a keyboard. + L = Keys.L, + + /// The Start Applications 1 button on a keyboard in Windows 2000/XP. + LaunchApplication1 = Keys.LaunchApplication1, + + /// The Start Applications 2 button on a keyboard in Windows 2000/XP. + LaunchApplication2 = Keys.LaunchApplication2, + + /// The Start Mail button on a keyboard in Windows 2000/XP. + LaunchMail = Keys.LaunchMail, + + /// The Left arrow button on a keyboard. + Left = Keys.Left, + + /// The Left Alt button on a keyboard. + LeftAlt = Keys.LeftAlt, + + /// The Left Control button on a keyboard. + LeftControl = Keys.LeftControl, + + /// The Left Shift button on a keyboard. + LeftShift = Keys.LeftShift, + + /// The Left Windows button on a keyboard. + LeftWindows = Keys.LeftWindows, + + /// The M button on a keyboard. + M = Keys.M, + + /// The MediaNextTrack button on a keyboard in Windows 2000/XP. + MediaNextTrack = Keys.MediaNextTrack, + + /// The MediaPlayPause button on a keyboard in Windows 2000/XP. + MediaPlayPause = Keys.MediaPlayPause, + + /// The MediaPreviousTrack button on a keyboard in Windows 2000/XP. + MediaPreviousTrack = Keys.MediaPreviousTrack, + + /// The MediaStop button on a keyboard in Windows 2000/XP. + MediaStop = Keys.MediaStop, + + /// The Multiply button on a keyboard. + Multiply = Keys.Multiply, + + /// The N button on a keyboard. + N = Keys.N, + + /// The Num Lock button on a keyboard. + NumLock = Keys.NumLock, + + /// The Numeric keypad 0 button on a keyboard. + NumPad0 = Keys.NumPad0, + + /// The Numeric keypad 1 button on a keyboard. + NumPad1 = Keys.NumPad1, + + /// The Numeric keypad 2 button on a keyboard. + NumPad2 = Keys.NumPad2, + + /// The Numeric keypad 3 button on a keyboard. + NumPad3 = Keys.NumPad3, + + /// The Numeric keypad 4 button on a keyboard. + NumPad4 = Keys.NumPad4, + + /// The Numeric keypad 5 button on a keyboard. + NumPad5 = Keys.NumPad5, + + /// The Numeric keypad 6 button on a keyboard. + NumPad6 = Keys.NumPad6, + + /// The Numeric keypad 7 button on a keyboard. + NumPad7 = Keys.NumPad7, + + /// The Numeric keypad 8 button on a keyboard. + NumPad8 = Keys.NumPad8, + + /// The Numeric keypad 9 button on a keyboard. + NumPad9 = Keys.NumPad9, + + /// The O button on a keyboard. + O = Keys.O, + + /// A miscellaneous button on a keyboard; can vary by keyboard. + Oem8 = Keys.Oem8, + + /// The OEM Auto button on a keyboard. + OemAuto = Keys.OemAuto, + + /// The OEM Angle Bracket or Backslash button on the RT 102 keyboard in Windows 2000/XP. + OemBackslash = Keys.OemBackslash, + + /// The Clear button on a keyboard. + OemClear = Keys.OemClear, + + /// The OEM Close Bracket button on a US standard keyboard in Windows 2000/XP. + OemCloseBrackets = Keys.OemCloseBrackets, + + /// The ',' button on a keyboard in any country/region in Windows 2000/XP. + OemComma = Keys.OemComma, + + /// The OEM Copy button on a keyboard. + OemCopy = Keys.OemCopy, + + /// The OEM Enlarge Window button on a keyboard. + OemEnlW = Keys.OemEnlW, + + /// The '-' button on a keyboard in any country/region in Windows 2000/XP. + OemMinus = Keys.OemMinus, + + /// The OEM Open Bracket button on a US standard keyboard in Windows 2000/XP. + OemOpenBrackets = Keys.OemOpenBrackets, + + /// The '.' button on a keyboard in any country/region. + OemPeriod = Keys.OemPeriod, + + /// The OEM Pipe button on a US standard keyboard. + OemPipe = Keys.OemPipe, + + /// The '+' button on a keyboard in Windows 2000/XP. + OemPlus = Keys.OemPlus, + + /// The OEM Question Mark button on a US standard keyboard. + OemQuestion = Keys.OemQuestion, + + /// The OEM Single/Double Quote button on a US standard keyboard. + OemQuotes = Keys.OemQuotes, + + /// The OEM Semicolon button on a US standard keyboard. + OemSemicolon = Keys.OemSemicolon, + + /// The OEM Tilde button on a US standard keyboard. + OemTilde = Keys.OemTilde, + + /// The P button on a keyboard. + P = Keys.P, + + /// The PA1 button on a keyboard. + Pa1 = Keys.Pa1, + + /// The Page Down button on a keyboard. + PageDown = Keys.PageDown, + + /// The Page Up button on a keyboard. + PageUp = Keys.PageUp, + + /// The Pause button on a keyboard. + Pause = Keys.Pause, + + /// The Play button on a keyboard. + Play = Keys.Play, + + /// The Print button on a keyboard. + Print = Keys.Print, + + /// The Print Screen button on a keyboard. + PrintScreen = Keys.PrintScreen, + + /// The IME Process button on a keyboard in Windows 95/98/ME/NT 4.0/2000/XP. + ProcessKey = Keys.ProcessKey, + + /// The Q button on a keyboard. + Q = Keys.Q, + + /// The R button on a keyboard. + R = Keys.R, + + /// The Right Arrow button on a keyboard. + Right = Keys.Right, + + /// The Right Alt button on a keyboard. + RightAlt = Keys.RightAlt, + + /// The Right Control button on a keyboard. + RightControl = Keys.RightControl, + + /// The Right Shift button on a keyboard. + RightShift = Keys.RightShift, + + /// The Right Windows button on a keyboard. + RightWindows = Keys.RightWindows, + + /// The S button on a keyboard. + S = Keys.S, + + /// The Scroll Lock button on a keyboard. + Scroll = Keys.Scroll, + + /// The Select button on a keyboard. + Select = Keys.Select, + + /// The Select Media button on a keyboard in Windows 2000/XP. + SelectMedia = Keys.SelectMedia, + + /// The Separator button on a keyboard. + Separator = Keys.Separator, + + /// The Computer Sleep button on a keyboard. + Sleep = Keys.Sleep, + + /// The Space bar on a keyboard. + Space = Keys.Space, + + /// The Subtract button on a keyboard. + Subtract = Keys.Subtract, + + /// The T button on a keyboard. + T = Keys.T, + + /// The Tab button on a keyboard. + Tab = Keys.Tab, + + /// The U button on a keyboard. + U = Keys.U, + + /// The Up Arrow button on a keyboard. + Up = Keys.Up, + + /// The V button on a keyboard. + V = Keys.V, + + /// The Volume Down button on a keyboard in Windows 2000/XP. + VolumeDown = Keys.VolumeDown, + + /// The Volume Mute button on a keyboard in Windows 2000/XP. + VolumeMute = Keys.VolumeMute, + + /// The Volume Up button on a keyboard in Windows 2000/XP. + VolumeUp = Keys.VolumeUp, + + /// The W button on a keyboard. + W = Keys.W, + + /// The X button on a keyboard. + X = Keys.X, + + /// The Y button on a keyboard. + Y = Keys.Y, + + /// The Z button on a keyboard. + Z = Keys.Z, + + /// The Zoom button on a keyboard. + Zoom = Keys.Zoom + } + + /// Provides extension methods for . +#if SMAPI_2_0 + public +#else + internal +#endif + static class SButtonExtensions + { + /********* + ** Accessors + *********/ + /// The offset added to values when converting them to to avoid collisions with values. + internal const int ControllerOffset = 2000; + + + /********* + ** Public methods + *********/ + /// Get the equivalent for the given button. + /// The keyboard button to convert. + internal static SButton ToSButton(this Keys key) + { + return (SButton)key; + } + + /// Get the equivalent for the given button. + /// The controller button to convert. + internal static SButton ToSButton(this Buttons key) + { + return (SButton)(SButtonExtensions.ControllerOffset + key); + } + + /// Get the equivalent for the given button. + /// The button to convert. + /// The keyboard equivalent. + /// Returns whether the value was converted successfully. + public static bool TryGetKeyboard(this SButton input, out Keys key) + { + if (Enum.IsDefined(typeof(Keys), (int)input)) + { + key = (Keys)input; + return true; + } + + key = Keys.None; + return false; + } + + /// Get the equivalent for the given button. + /// The button to convert. + /// The controller equivalent. + /// Returns whether the value was converted successfully. + public static bool TryGetController(this SButton input, out Buttons button) + { + if (Enum.IsDefined(typeof(Keys), (int)input - SButtonExtensions.ControllerOffset)) + { + button = (Buttons)(input - SButtonExtensions.ControllerOffset); + return true; + } + + button = 0; + return false; + } + } +} -- cgit