From 95a93a05b39d2b27b538ecdb0e6a18f28096c5c2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 7 Feb 2017 20:50:41 -0500 Subject: remove oldest deprecated code (#231) Since Stardew Valley 1.2 breaks most mods anyway, this commits removes the oldest deprecations and fixes the issues that are easiest for mods to update. See documentation for details. --- src/StardewModdingAPI/Command.cs | 9 - src/StardewModdingAPI/Constants.cs | 6 +- src/StardewModdingAPI/Entities/SPlayer.cs | 59 - src/StardewModdingAPI/Events/ChangeType.cs | 15 + .../Events/EventArgsInventoryChanged.cs | 1 - src/StardewModdingAPI/Events/GraphicsEvents.cs | 2 +- src/StardewModdingAPI/Events/ItemStackChange.cs | 20 + src/StardewModdingAPI/Events/PlayerEvents.cs | 1 - src/StardewModdingAPI/Extensions.cs | 194 ---- src/StardewModdingAPI/Framework/SGame.cs | 1133 +++++++++++++++++++ .../Serialisation/SemanticVersionConverter.cs | 51 + src/StardewModdingAPI/Inheritance/ChangeType.cs | 15 - .../Inheritance/ItemStackChange.cs | 20 - src/StardewModdingAPI/Inheritance/SGame.cs | 1134 -------------------- src/StardewModdingAPI/Inheritance/SObject.cs | 249 ----- src/StardewModdingAPI/LogWriter.cs | 66 -- src/StardewModdingAPI/Manifest.cs | 39 +- src/StardewModdingAPI/Mod.cs | 18 +- src/StardewModdingAPI/Program.cs | 46 +- src/StardewModdingAPI/SemanticVersion.cs | 19 +- src/StardewModdingAPI/StardewModdingAPI.csproj | 12 +- src/StardewModdingAPI/Version.cs | 121 --- src/TrainerMod/TrainerMod.cs | 12 - 23 files changed, 1253 insertions(+), 1989 deletions(-) delete mode 100644 src/StardewModdingAPI/Entities/SPlayer.cs create mode 100644 src/StardewModdingAPI/Events/ChangeType.cs create mode 100644 src/StardewModdingAPI/Events/ItemStackChange.cs delete mode 100644 src/StardewModdingAPI/Extensions.cs create mode 100644 src/StardewModdingAPI/Framework/SGame.cs create mode 100644 src/StardewModdingAPI/Framework/Serialisation/SemanticVersionConverter.cs delete mode 100644 src/StardewModdingAPI/Inheritance/ChangeType.cs delete mode 100644 src/StardewModdingAPI/Inheritance/ItemStackChange.cs delete mode 100644 src/StardewModdingAPI/Inheritance/SGame.cs delete mode 100644 src/StardewModdingAPI/Inheritance/SObject.cs delete mode 100644 src/StardewModdingAPI/LogWriter.cs delete mode 100644 src/StardewModdingAPI/Version.cs (limited to 'src') diff --git a/src/StardewModdingAPI/Command.cs b/src/StardewModdingAPI/Command.cs index 1fa18d49..6195bd8b 100644 --- a/src/StardewModdingAPI/Command.cs +++ b/src/StardewModdingAPI/Command.cs @@ -67,15 +67,6 @@ namespace StardewModdingAPI /**** ** SMAPI ****/ - /// Parse a command string and invoke it if valid. - /// The command to run, including the command name and any arguments. - [Obsolete("Use the overload which passes in your mod's monitor")] - public static void CallCommand(string input) - { - Program.DeprecationManager.Warn($"an old version of {nameof(Command)}.{nameof(Command.CallCommand)}", "1.1", DeprecationLevel.Notice); - Command.CallCommand(input, Program.GetLegacyMonitorForMod()); - } - /// Parse a command string and invoke it if valid. /// The command to run, including the command name and any arguments. /// Encapsulates monitoring and logging. diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index a62a0d58..d3c2ddcc 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -26,11 +26,7 @@ namespace StardewModdingAPI ** Accessors *********/ /// SMAPI's current semantic version. - [Obsolete("Use " + nameof(Constants) + "." + nameof(ApiVersion))] - public static readonly Version Version = (Version)Constants.ApiVersion; - - /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion => new Version(1, 8, 0, null, suppressDeprecationWarning: true); + public static ISemanticVersion ApiVersion => new SemanticVersion(1, 8, 0, null); /// The minimum supported version of Stardew Valley. public const string MinimumGameVersion = "1.1"; diff --git a/src/StardewModdingAPI/Entities/SPlayer.cs b/src/StardewModdingAPI/Entities/SPlayer.cs deleted file mode 100644 index 66c7ba44..00000000 --- a/src/StardewModdingAPI/Entities/SPlayer.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using StardewModdingAPI.Framework; -using StardewValley; - -namespace StardewModdingAPI.Entities -{ - /// Static class for integrating with the player. - [Obsolete("This API was never officially documented and will be removed soon.")] - public class SPlayer - { - /********* - ** Accessors - *********/ - /// Obsolete. - [Obsolete("Use " + nameof(Game1) + "." + nameof(Game1.getAllFarmers) + " instead")] - public static List AllFarmers - { - get - { - Program.DeprecationManager.Warn(nameof(SPlayer), "1.0", DeprecationLevel.Info); - return Game1.getAllFarmers(); - } - } - - /// Obsolete. - [Obsolete("Use " + nameof(Game1) + "." + nameof(Game1.player) + " instead")] - public static Farmer CurrentFarmer - { - get - { - Program.DeprecationManager.Warn(nameof(SPlayer), "1.0", DeprecationLevel.Info); - return Game1.player; - } - } - - /// Obsolete. - [Obsolete("Use " + nameof(Game1) + "." + nameof(Game1.player) + " instead")] - public static Farmer Player - { - get - { - Program.DeprecationManager.Warn(nameof(SPlayer), "1.0", DeprecationLevel.Info); - return Game1.player; - } - } - - /// Obsolete. - [Obsolete("Use " + nameof(Game1) + "." + nameof(Game1.player) + "." + nameof(Farmer.currentLocation) + " instead")] - public static GameLocation CurrentFarmerLocation - { - get - { - Program.DeprecationManager.Warn(nameof(SPlayer), "1.0", DeprecationLevel.Info); - return Game1.player.currentLocation; - } - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/ChangeType.cs b/src/StardewModdingAPI/Events/ChangeType.cs new file mode 100644 index 00000000..4b207f08 --- /dev/null +++ b/src/StardewModdingAPI/Events/ChangeType.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Events +{ + /// Indicates how an inventory item changed. + public enum ChangeType + { + /// The entire stack was removed. + Removed, + + /// The entire stack was added. + Added, + + /// The stack size changed. + StackChange + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/EventArgsInventoryChanged.cs b/src/StardewModdingAPI/Events/EventArgsInventoryChanged.cs index 40c77419..11cbcedf 100644 --- a/src/StardewModdingAPI/Events/EventArgsInventoryChanged.cs +++ b/src/StardewModdingAPI/Events/EventArgsInventoryChanged.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using StardewModdingAPI.Inheritance; using StardewValley; namespace StardewModdingAPI.Events diff --git a/src/StardewModdingAPI/Events/GraphicsEvents.cs b/src/StardewModdingAPI/Events/GraphicsEvents.cs index 5f4feeac..c3236c0a 100644 --- a/src/StardewModdingAPI/Events/GraphicsEvents.cs +++ b/src/StardewModdingAPI/Events/GraphicsEvents.cs @@ -15,7 +15,7 @@ namespace StardewModdingAPI.Events /// Raised after the game window is resized. public static event EventHandler Resize; - /// Raised when drawing debug information to the screen (when is true). This is called after the sprite batch is begun. If you just want to add debug info, use in your update loop. + /// Raised when drawing debug information to the screen (when is true). This is called after the sprite batch is begun. If you just want to add debug info, use in your update loop. public static event EventHandler DrawDebug; /// Obsolete. diff --git a/src/StardewModdingAPI/Events/ItemStackChange.cs b/src/StardewModdingAPI/Events/ItemStackChange.cs new file mode 100644 index 00000000..f9ae6df6 --- /dev/null +++ b/src/StardewModdingAPI/Events/ItemStackChange.cs @@ -0,0 +1,20 @@ +using StardewValley; + +namespace StardewModdingAPI.Events +{ + /// Represents an inventory slot that changed. + public class ItemStackChange + { + /********* + ** Accessors + *********/ + /// The item in the slot. + public Item Item { get; set; } + + /// The amount by which the item's stack size changed. + public int StackChange { get; set; } + + /// How the inventory slot changed. + public ChangeType ChangeType { get; set; } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/PlayerEvents.cs b/src/StardewModdingAPI/Events/PlayerEvents.cs index dd3ff220..6e65f5ae 100644 --- a/src/StardewModdingAPI/Events/PlayerEvents.cs +++ b/src/StardewModdingAPI/Events/PlayerEvents.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using StardewModdingAPI.Framework; -using StardewModdingAPI.Inheritance; using StardewValley; namespace StardewModdingAPI.Events diff --git a/src/StardewModdingAPI/Extensions.cs b/src/StardewModdingAPI/Extensions.cs deleted file mode 100644 index 0e9dbbf7..00000000 --- a/src/StardewModdingAPI/Extensions.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; -using StardewModdingAPI.Framework; - -namespace StardewModdingAPI -{ - /// Provides general utility extensions. - public static class Extensions - { - /********* - ** Properties - *********/ - /// The backing field for . - private static readonly Random _random = new Random(); - - - /********* - ** Accessors - *********/ - /// A pseudo-random number generator. - public static Random Random - { - get - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Random)}", "1.0", DeprecationLevel.PendingRemoval); - return Extensions._random; - } - } - - - /********* - ** Public methods - *********/ - /// Get whether the given key is currently being pressed. - /// The key to check. - public static bool IsKeyDown(this Keys key) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsKeyDown)}", "1.0", DeprecationLevel.PendingRemoval); - - return Keyboard.GetState().IsKeyDown(key); - } - - /// Get a random color. - public static Color RandomColour() - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RandomColour)}", "1.0", DeprecationLevel.PendingRemoval); - - return new Color(Extensions.Random.Next(0, 255), Extensions.Random.Next(0, 255), Extensions.Random.Next(0, 255)); - } - - /// Concatenate an enumeration into a delimiter-separated string. - /// The values to concatenate. - /// The value separator. - [Obsolete("The usage of ToSingular has changed. Please update your call to use ToSingular")] - public static string ToSingular(this IEnumerable ienum, string split = ", ") - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "0.39.3", DeprecationLevel.PendingRemoval); - return ""; - } - - /// Concatenate an enumeration into a delimiter-separated string. - /// The enumerated value type. - /// The values to concatenate. - /// The value separator. - public static string ToSingular(this IEnumerable ienum, string split = ", ") - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "1.0", DeprecationLevel.PendingRemoval); - - //Apparently Keys[] won't split normally :l - if (typeof(T) == typeof(Keys)) - { - return string.Join(split, ienum.ToArray()); - } - return string.Join(split, ienum); - } - - /// Get whether the value can be parsed as a number. - /// The value. - public static bool IsInt32(this object o) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsInt32)}", "1.0", DeprecationLevel.PendingRemoval); - - int i; - return int.TryParse(o.ToString(), out i); - } - - /// Get the numeric representation of a value. - /// The value. - public static int AsInt32(this object o) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsInt32)}", "1.0", DeprecationLevel.PendingRemoval); - - return int.Parse(o.ToString()); - } - - /// Get whether the value can be parsed as a boolean. - /// The value. - public static bool IsBool(this object o) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsBool)}", "1.0", DeprecationLevel.PendingRemoval); - - bool b; - return bool.TryParse(o.ToString(), out b); - } - - /// Get the boolean representation of a value. - /// The value. - public static bool AsBool(this object o) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsBool)}", "1.0", DeprecationLevel.PendingRemoval); - - return bool.Parse(o.ToString()); - } - - /// Get a list hash calculated from the hashes of the values it contains. - /// The values to hash. - public static int GetHash(this IEnumerable enumerable) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetHash)}", "1.0", DeprecationLevel.PendingRemoval); - - var hash = 0; - foreach (var v in enumerable) - hash ^= v.GetHashCode(); - return hash; - } - - /// Cast a value to the given type. This returns null if the value can't be cast. - /// The type to which to cast. - /// The value. - public static T Cast(this object o) where T : class - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Cast)}", "1.0", DeprecationLevel.PendingRemoval); - - return o as T; - } - - /// Get all private types on an object. - /// The object to scan. - public static FieldInfo[] GetPrivateFields(this object o) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetPrivateFields)}", "1.0", DeprecationLevel.PendingRemoval); - return o.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); - } - - /// Get metadata for a private field. - /// The type to scan. - /// The name of the field to find. - public static FieldInfo GetBaseFieldInfo(this Type t, string name) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0", DeprecationLevel.PendingRemoval); - return t.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); - } - - /// Get the value of a private field. - /// The type to scan. - /// The instance for which to get a value. - /// The name of the field to find. - public static T GetBaseFieldValue(this Type t, object o, string name) where T : class - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0", DeprecationLevel.PendingRemoval); - return t.GetBaseFieldInfo(name).GetValue(o) as T; - } - - /// Set the value of a private field. - /// The type to scan. - /// The instance for which to set a value. - /// The name of the field to find. - /// The value to set. - public static void SetBaseFieldValue(this Type t, object o, string name, object newValue) where T : class - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.SetBaseFieldValue)}", "1.0", DeprecationLevel.PendingRemoval); - t.GetBaseFieldInfo(name).SetValue(o, newValue as T); - } - - /// Get a copy of the string with only alphanumeric characters. (Numbers are not removed, despite the name.) - /// The string to copy. - public static string RemoveNumerics(this string st) - { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RemoveNumerics)}", "1.0", DeprecationLevel.PendingRemoval); - var s = st; - foreach (var c in s) - { - if (!char.IsLetterOrDigit(c)) - s = s.Replace(c.ToString(), ""); - } - return s; - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs new file mode 100644 index 00000000..2486e376 --- /dev/null +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -0,0 +1,1133 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.BellsAndWhistles; +using StardewValley.Locations; +using StardewValley.Menus; +using StardewValley.Tools; +using xTile.Dimensions; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace StardewModdingAPI.Framework +{ + /// SMAPI's extension of the game's core , used to inject events. + internal class SGame : Game1 + { + /********* + ** Properties + *********/ + /// The number of ticks until SMAPI should notify mods when is set. + /// Skipping a few frames ensures the game finishes initialising the world before mods try to change it. + private int AfterLoadTimer = 5; + + /// Whether the player has loaded a save and the world has finished initialising. + private bool IsWorldReady => this.AfterLoadTimer < 0; + + /// The debug messages to add to the next debug output. + internal static Queue DebugMessageQueue { get; private set; } + + /// Whether the game's zoom level is at 100% (i.e. nothing should be scaled). + public bool ZoomLevelIsOne => Game1.options.zoomLevel.Equals(1.0f); + + /// Encapsulates monitoring and logging. + private readonly IMonitor Monitor; + + + /********* + ** Accessors + *********/ + /// Arrays of pressed controller buttons indexed by . + public Buttons[][] PreviouslyPressedButtons; + + /// A record of the keyboard state (i.e. the up/down state for each button) as of the latest tick. + public KeyboardState KStateNow { get; private set; } + + /// A record of the keyboard state (i.e. the up/down state for each button) as of the previous tick. + public KeyboardState KStatePrior { get; private set; } + + /// A record of the mouse state (i.e. the cursor position, scroll amount, and the up/down state for each button) as of the latest tick. + public MouseState MStateNow { get; private set; } + + /// 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. + public MouseState MStatePrior { get; private set; } + + /// The current mouse position on the screen adjusted for the zoom level. + public Point MPositionNow { get; private set; } + + /// The previous mouse position on the screen adjusted for the zoom level. + public Point MPositionPrior { get; private set; } + + /// The keys that were pressed as of the latest tick. + public Keys[] CurrentlyPressedKeys => this.KStateNow.GetPressedKeys(); + + /// The keys that were pressed as of the previous tick. + public Keys[] PreviouslyPressedKeys => this.KStatePrior.GetPressedKeys(); + + /// The keys that just entered the down state. + public Keys[] FramePressedKeys => this.CurrentlyPressedKeys.Except(this.PreviouslyPressedKeys).ToArray(); + + /// The keys that just entered the up state. + public Keys[] FrameReleasedKeys => this.PreviouslyPressedKeys.Except(this.CurrentlyPressedKeys).ToArray(); + + /// Whether a save is currently loaded at last check. + public bool PreviouslyLoadedGame { get; private set; } + + /// A hash of at last check. + public int PreviousGameLocations { get; private set; } + + /// A hash of the current location's at last check. + public int PreviousLocationObjects { get; private set; } + + /// The player's inventory at last check. + public Dictionary PreviousItems { get; private set; } + + /// The player's combat skill level at last check. + public int PreviousCombatLevel { get; private set; } + + /// The player's farming skill level at last check. + public int PreviousFarmingLevel { get; private set; } + + /// The player's fishing skill level at last check. + public int PreviousFishingLevel { get; private set; } + + /// The player's foraging skill level at last check. + public int PreviousForagingLevel { get; private set; } + + /// The player's mining skill level at last check. + public int PreviousMiningLevel { get; private set; } + + /// The player's luck skill level at last check. + public int PreviousLuckLevel { get; private set; } + + /// The player's location at last check. + public GameLocation PreviousGameLocation { get; private set; } + + /// The active game menu at last check. + public IClickableMenu PreviousActiveMenu { get; private set; } + + /// The mine level at last check. + public int PreviousMineLevel { get; private set; } + + /// The time of day (in 24-hour military format) at last check. + public int PreviousTimeOfDay { get; private set; } + + /// The day of month (1–28) at last check. + public int PreviousDayOfMonth { get; private set; } + + /// The season name (winter, spring, summer, or fall) at last check. + public string PreviousSeasonOfYear { get; private set; } + + /// The year number at last check. + public int PreviousYearOfGame { get; private set; } + + /// Whether the game was transitioning to a new day at last check. + public bool PreviousIsNewDay { get; private set; } + + /// The player character at last check. + public Farmer PreviousFarmer { get; private set; } + + /// An index incremented on every tick and reset every 60th tick (0–59). + public int CurrentUpdateTick { get; private set; } + + /// Whether this is the very first update tick since the game started. + public bool FirstUpdate { get; private set; } + + /// The game's current render target. + public RenderTarget2D Screen + { + get { return this.GetBaseFieldValue("screen"); } + set { this.SetBaseFieldValue("screen", value); } + } + + /// The game's current background color. + public Color BgColour + { + get { return (Color)this.GetBaseFieldValue("bgColor"); } + set { this.SetBaseFieldValue("bgColor", value); } + } + + /// The current game instance. + public static SGame Instance { get; private set; } + + /// The game's current frame rate, recalculated on each draw update. + public static float FramesPerSecond { get; private set; } + + /// Whether we're in pseudo-debug mode, which shows information like FPS. + public static bool Debug { get; private set; } + + /// The current player. + [Obsolete("Use Game1.player instead")] + public Farmer CurrentFarmer => Game1.player; + + /// The game method which draws the farm buildings. + public static MethodInfo DrawFarmBuildings = typeof(Game1).GetMethod("drawFarmBuildings", BindingFlags.NonPublic | BindingFlags.Instance); + + /// The game method which draws the game HUD. + public static MethodInfo DrawHUD = typeof(Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance); + + /// The game method which draws the current dialogue box, if any. + public static MethodInfo DrawDialogueBox = typeof(Game1).GetMethod("drawDialogueBox", BindingFlags.NonPublic | BindingFlags.Instance); + + + /********* + ** Public methods + *********/ + /// Get the controller buttons which are currently pressed. + /// The controller to check. + public Buttons[] GetButtonsDown(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.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); + } + return buttons.ToArray(); + } + + /// Get the controller buttons which were pressed after the last update. + /// The controller to check. + public Buttons[] GetFramePressedButtons(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.IsConnected) + { + if (this.WasButtonJustPressed(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); + if (this.WasButtonJustPressed(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); + if (this.WasButtonJustPressed(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); + if (this.WasButtonJustPressed(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); + if (this.WasButtonJustPressed(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); + if (this.WasButtonJustPressed(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); + if (this.WasButtonJustPressed(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); + if (this.WasButtonJustPressed(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); + if (this.WasButtonJustPressed(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); + if (this.WasButtonJustPressed(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); + if (this.WasButtonJustPressed(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); + if (this.WasButtonJustPressed(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); + if (this.WasButtonJustPressed(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); + if (this.WasButtonJustPressed(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); + if (this.WasButtonJustPressed(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); + if (this.WasButtonJustPressed(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); + if (this.WasButtonJustPressed(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); + } + return buttons.ToArray(); + } + + /// Get the controller buttons which were released after the last update. + /// The controller to check. + public Buttons[] GetFrameReleasedButtons(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.IsConnected) + { + if (this.WasButtonJustReleased(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); + if (this.WasButtonJustReleased(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); + if (this.WasButtonJustReleased(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); + if (this.WasButtonJustReleased(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); + if (this.WasButtonJustReleased(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); + if (this.WasButtonJustReleased(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); + if (this.WasButtonJustReleased(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); + if (this.WasButtonJustReleased(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); + if (this.WasButtonJustReleased(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); + if (this.WasButtonJustReleased(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); + if (this.WasButtonJustReleased(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); + if (this.WasButtonJustReleased(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); + if (this.WasButtonJustReleased(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); + if (this.WasButtonJustReleased(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); + if (this.WasButtonJustReleased(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); + if (this.WasButtonJustReleased(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); + if (this.WasButtonJustReleased(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); + } + return buttons.ToArray(); + } + + /// Queue a message to be added to the debug output. + /// The message to add. + /// Returns whether the message was successfully queued. + public static bool QueueDebugMessage(string message) + { + if (!SGame.Debug) + return false; + if (SGame.DebugMessageQueue.Count > 32) + return false; + + SGame.DebugMessageQueue.Enqueue(message); + return true; + } + + + /********* + ** Protected methods + *********/ + /// Construct an instance. + /// Encapsulates monitoring and logging. + internal SGame(IMonitor monitor) + { + this.Monitor = monitor; + this.FirstUpdate = true; + SGame.Instance = this; + } + + /// The method called during game launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. + protected override void Initialize() + { + //ModItems = new Dictionary(); + SGame.DebugMessageQueue = new Queue(); + this.PreviouslyPressedButtons = new Buttons[4][]; + for (var i = 0; i < 4; ++i) + this.PreviouslyPressedButtons[i] = new Buttons[0]; + + base.Initialize(); + GameEvents.InvokeInitialize(this.Monitor); + } + + /// The method called before XNA or MonoGame loads or reloads graphics resources. + protected override void LoadContent() + { + base.LoadContent(); + GameEvents.InvokeLoadContent(this.Monitor); + } + + /// The method called when the game is updating its state. This happens roughly 60 times per second. + /// A snapshot of the game timing state. + protected override void Update(GameTime gameTime) + { + // add FPS to debug output + SGame.QueueDebugMessage($"FPS: {SGame.FramesPerSecond}"); + + // raise game loaded + if (this.FirstUpdate) + GameEvents.InvokeGameLoaded(this.Monitor); + + // update SMAPI events + this.UpdateEventCalls(); + + // toggle debug output + if (this.FramePressedKeys.Contains(Keys.F3)) + SGame.Debug = !SGame.Debug; + + // let game update + try + { + base.Update(gameTime); + } + catch (Exception ex) + { + this.Monitor.Log($"An error occured in the base update loop: {ex.GetLogSummary()}", LogLevel.Error); + Console.ReadKey(); + } + + // raise update events + GameEvents.InvokeUpdateTick(this.Monitor); + if (this.FirstUpdate) + { + GameEvents.InvokeFirstUpdateTick(this.Monitor); + this.FirstUpdate = false; + } + if (this.CurrentUpdateTick % 2 == 0) + GameEvents.InvokeSecondUpdateTick(this.Monitor); + if (this.CurrentUpdateTick % 4 == 0) + GameEvents.InvokeFourthUpdateTick(this.Monitor); + if (this.CurrentUpdateTick % 8 == 0) + GameEvents.InvokeEighthUpdateTick(this.Monitor); + if (this.CurrentUpdateTick % 15 == 0) + GameEvents.InvokeQuarterSecondTick(this.Monitor); + if (this.CurrentUpdateTick % 30 == 0) + GameEvents.InvokeHalfSecondTick(this.Monitor); + if (this.CurrentUpdateTick % 60 == 0) + GameEvents.InvokeOneSecondTick(this.Monitor); + this.CurrentUpdateTick += 1; + if (this.CurrentUpdateTick >= 60) + this.CurrentUpdateTick = 0; + + // track keyboard state + if (this.KStatePrior != this.KStateNow) + this.KStatePrior = this.KStateNow; + + // track controller button state + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + this.PreviouslyPressedButtons[(int)i] = this.GetButtonsDown(i); + } + + /// The method called to draw everything to the screen. + /// A snapshot of the game timing state. + /// This implementation is identical to , except for try..catch around menu draw code, minor formatting, and added events. + protected override void Draw(GameTime gameTime) + { + // track frame rate + SGame.FramesPerSecond = 1 / (float)gameTime.ElapsedGameTime.TotalSeconds; + + try + { + if (!this.ZoomLevelIsOne) + this.GraphicsDevice.SetRenderTarget(this.Screen); + + this.GraphicsDevice.Clear(this.BgColour); + if (Game1.options.showMenuBackground && Game1.activeClickableMenu != null && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet()) + { + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + try + { + Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing its background. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + GraphicsEvents.InvokeOnPreRenderGuiEvent(this.Monitor); + try + { + Game1.activeClickableMenu.draw(Game1.spriteBatch); + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + GraphicsEvents.InvokeOnPostRenderGuiEvent(this.Monitor); + Game1.spriteBatch.End(); + if (!this.ZoomLevelIsOne) + { + this.GraphicsDevice.SetRenderTarget(null); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + } + return; + } + if (Game1.gameMode == 11) + { + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + Game1.spriteBatch.DrawString(Game1.smoothFont, "Stardew Valley has crashed...", new Vector2(16f, 16f), Color.HotPink); + Game1.spriteBatch.DrawString(Game1.smoothFont, "Please send the error report or a screenshot of this message to @ConcernedApe. (http://stardewvalley.net/contact/)", new Vector2(16f, 32f), new Color(0, 255, 0)); + Game1.spriteBatch.DrawString(Game1.smoothFont, Game1.parseText(Game1.errorMessage, Game1.smoothFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White); + Game1.spriteBatch.End(); + return; + } + if (Game1.currentMinigame != null) + { + Game1.currentMinigame.draw(Game1.spriteBatch); + if (Game1.globalFade && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause)) + { + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); + Game1.spriteBatch.End(); + } + if (!this.ZoomLevelIsOne) + { + this.GraphicsDevice.SetRenderTarget(null); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + } + return; + } + if (Game1.showingEndOfNightStuff) + { + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + try + { + Game1.activeClickableMenu?.draw(Game1.spriteBatch); + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + Game1.spriteBatch.End(); + if (!this.ZoomLevelIsOne) + { + this.GraphicsDevice.SetRenderTarget(null); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + } + return; + } + if (Game1.gameMode == 6) + { + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + string text = ""; + int num = 0; + while (num < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0) + { + text += "."; + num++; + } + SpriteText.drawString(Game1.spriteBatch, "Loading" + text, 64, Game1.graphics.GraphicsDevice.Viewport.Height - 64, 999, -1, 999, 1f, 1f, false, 0, "Loading..."); + Game1.spriteBatch.End(); + if (!this.ZoomLevelIsOne) + { + this.GraphicsDevice.SetRenderTarget(null); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + } + return; + } + if (Game1.gameMode == 0) + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + else + { + if (Game1.drawLighting) + { + this.GraphicsDevice.SetRenderTarget(Game1.lightmap); + this.GraphicsDevice.Clear(Color.White * 0f); + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null); + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.lightmap.Bounds, Game1.currentLocation.name.Equals("UndergroundMine") ? Game1.mine.getLightingColor(gameTime) : ((!Game1.ambientLight.Equals(Color.White) && (!Game1.isRaining || !Game1.currentLocation.isOutdoors)) ? Game1.ambientLight : Game1.outdoorLight)); + for (int i = 0; i < Game1.currentLightSources.Count; i++) + { + if (Utility.isOnScreen(Game1.currentLightSources.ElementAt(i).position, (int)(Game1.currentLightSources.ElementAt(i).radius * Game1.tileSize * 4f))) + Game1.spriteBatch.Draw(Game1.currentLightSources.ElementAt(i).lightTexture, Game1.GlobalToLocal(Game1.viewport, Game1.currentLightSources.ElementAt(i).position) / Game1.options.lightingQuality, Game1.currentLightSources.ElementAt(i).lightTexture.Bounds, Game1.currentLightSources.ElementAt(i).color, 0f, new Vector2(Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.X, Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.Y), Game1.currentLightSources.ElementAt(i).radius / Game1.options.lightingQuality, SpriteEffects.None, 0.9f); + } + Game1.spriteBatch.End(); + this.GraphicsDevice.SetRenderTarget(this.ZoomLevelIsOne ? null : this.Screen); + } + if (Game1.bloomDay) + Game1.bloom?.BeginDraw(); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + GraphicsEvents.InvokeOnPreRenderEvent(this.Monitor); + Game1.background?.draw(Game1.spriteBatch); + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + Game1.currentLocation.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); + Game1.currentLocation.drawWater(Game1.spriteBatch); + if (Game1.CurrentEvent == null) + { + using (List.Enumerator enumerator = Game1.currentLocation.characters.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + NPC current = enumerator.Current; + if (current != null && !current.swimming && !current.hideShadow && !current.IsMonster && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current.position + new Vector2(current.sprite.spriteWidth * Game1.pixelZoom / 2f, current.GetBoundingBox().Height + (current.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current.yJumpOffset / 40f) * current.scale, SpriteEffects.None, Math.Max(0f, current.getStandingY() / 10000f) - 1E-06f); + } + goto IL_B30; + } + } + foreach (NPC current2 in Game1.CurrentEvent.actors) + { + if (!current2.swimming && !current2.hideShadow && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current2.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current2.position + new Vector2(current2.sprite.spriteWidth * Game1.pixelZoom / 2f, current2.GetBoundingBox().Height + (current2.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current2.yJumpOffset / 40f) * current2.scale, SpriteEffects.None, Math.Max(0f, current2.getStandingY() / 10000f) - 1E-06f); + } + IL_B30: + if (!Game1.player.swimming && !Game1.player.isRidingHorse() && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(Game1.player.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.player.position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((Game1.player.running || Game1.player.usingTool) && Game1.player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[Game1.player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, 0f); + Game1.currentLocation.Map.GetLayer("Buildings").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); + Game1.mapDisplayDevice.EndScene(); + Game1.spriteBatch.End(); + Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (Game1.CurrentEvent == null) + { + using (List.Enumerator enumerator3 = Game1.currentLocation.characters.GetEnumerator()) + { + while (enumerator3.MoveNext()) + { + NPC current3 = enumerator3.Current; + if (current3 != null && !current3.swimming && !current3.hideShadow && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current3.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current3.position + new Vector2(current3.sprite.spriteWidth * Game1.pixelZoom / 2f, current3.GetBoundingBox().Height + (current3.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current3.yJumpOffset / 40f) * current3.scale, SpriteEffects.None, Math.Max(0f, current3.getStandingY() / 10000f) - 1E-06f); + } + goto IL_F5F; + } + } + foreach (NPC current4 in Game1.CurrentEvent.actors) + { + if (!current4.swimming && !current4.hideShadow && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current4.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current4.position + new Vector2(current4.sprite.spriteWidth * Game1.pixelZoom / 2f, current4.GetBoundingBox().Height + (current4.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current4.yJumpOffset / 40f) * current4.scale, SpriteEffects.None, Math.Max(0f, current4.getStandingY() / 10000f) - 1E-06f); + } + IL_F5F: + if (!Game1.player.swimming && !Game1.player.isRidingHorse() && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(Game1.player.getTileLocation())) + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.player.position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((Game1.player.running || Game1.player.usingTool) && Game1.player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[Game1.player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, Math.Max(0.0001f, Game1.player.getStandingY() / 10000f + 0.00011f) - 0.0001f); + if (Game1.displayFarmer) + Game1.player.draw(Game1.spriteBatch); + if ((Game1.eventUp || Game1.killScreen) && !Game1.killScreen) + Game1.currentLocation.currentEvent?.draw(Game1.spriteBatch); + if (Game1.player.currentUpgrade != null && Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3 && Game1.currentLocation.Name.Equals("Farm")) + Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), Game1.player.currentUpgrade.getSourceRectangle(), Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + Game1.tileSize * 3 / 4) / 10000f); + Game1.currentLocation.draw(Game1.spriteBatch); + if (Game1.eventUp && Game1.currentLocation.currentEvent?.messageToScreen != null) + Game1.drawWithBorder(Game1.currentLocation.currentEvent.messageToScreen, Color.Black, Color.White, new Vector2(Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width / 2 - Game1.borderFont.MeasureString(Game1.currentLocation.currentEvent.messageToScreen).X / 2f, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Height - Game1.tileSize), 0f, 1f, 0.999f); + if (Game1.player.ActiveObject == null && (Game1.player.UsingTool || Game1.pickingTool) && Game1.player.CurrentTool != null && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool)) + Game1.drawTool(Game1.player); + if (Game1.currentLocation.Name.Equals("Farm")) + SGame.DrawFarmBuildings.Invoke(Program.gamePtr, null); + if (Game1.tvStation >= 0) + Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(6 * Game1.tileSize + Game1.tileSize / 4, 2 * Game1.tileSize + Game1.tileSize / 2)), new Rectangle(Game1.tvStation * 24, 0, 24, 15), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1E-08f); + if (Game1.panMode) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((int)Math.Floor((Game1.getOldMouseX() + Game1.viewport.X) / (double)Game1.tileSize) * Game1.tileSize - Game1.viewport.X, (int)Math.Floor((Game1.getOldMouseY() + Game1.viewport.Y) / (double)Game1.tileSize) * Game1.tileSize - Game1.viewport.Y, Game1.tileSize, Game1.tileSize), Color.Lime * 0.75f); + foreach (Warp current5 in Game1.currentLocation.warps) + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(current5.X * Game1.tileSize - Game1.viewport.X, current5.Y * Game1.tileSize - Game1.viewport.Y, Game1.tileSize, Game1.tileSize), Color.Red * 0.75f); + } + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + Game1.currentLocation.Map.GetLayer("Front").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); + Game1.mapDisplayDevice.EndScene(); + Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch); + Game1.spriteBatch.End(); + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (Game1.currentLocation.Name.Equals("Farm") && Game1.stats.SeedsSown >= 200u) + { + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(3 * Game1.tileSize + Game1.tileSize / 4, Game1.tileSize + Game1.tileSize / 3)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize + Game1.tileSize, 2 * Game1.tileSize + Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(5 * Game1.tileSize, 2 * Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(3 * Game1.tileSize + Game1.tileSize / 2, 3 * Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(5 * Game1.tileSize - Game1.tileSize / 4, Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize, 3 * Game1.tileSize + Game1.tileSize / 6)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize + Game1.tileSize / 5, 2 * Game1.tileSize + Game1.tileSize / 3)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); + } + if (Game1.displayFarmer && Game1.player.ActiveObject != null && Game1.player.ActiveObject.bigCraftable && this.checkBigCraftableBoundariesForFrontLayer() && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null) + Game1.drawPlayerHeldObject(Game1.player); + else if (Game1.displayFarmer && Game1.player.ActiveObject != null && ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.position.X, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.position.X, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")) || (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")))) + Game1.drawPlayerHeldObject(Game1.player); + if ((Game1.player.UsingTool || Game1.pickingTool) && Game1.player.CurrentTool != null && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool) && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null) + Game1.drawTool(Game1.player); + if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null) + { + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + Game1.currentLocation.Map.GetLayer("AlwaysFront").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); + Game1.mapDisplayDevice.EndScene(); + } + if (Game1.toolHold > 400f && Game1.player.CurrentTool.UpgradeLevel >= 1 && Game1.player.canReleaseTool) + { + Color color = Color.White; + switch ((int)(Game1.toolHold / 600f) + 2) + { + case 1: + color = Tool.copperColor; + break; + case 2: + color = Tool.steelColor; + break; + case 3: + color = Tool.goldColor; + break; + case 4: + color = Tool.iridiumColor; + break; + } + Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X - 2, (int)Game1.player.getLocalPosition(Game1.viewport).Y - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : Game1.tileSize) - 2, (int)(Game1.toolHold % 600f * 0.08f) + 4, Game1.tileSize / 8 + 4), Color.Black); + Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, (int)Game1.player.getLocalPosition(Game1.viewport).Y - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : Game1.tileSize), (int)(Game1.toolHold % 600f * 0.08f), Game1.tileSize / 8), color); + } + if (Game1.isDebrisWeather && Game1.currentLocation.IsOutdoors && !Game1.currentLocation.ignoreDebrisWeather && !Game1.currentLocation.Name.Equals("Desert") && Game1.viewport.X > -10) + { + foreach (WeatherDebris current6 in Game1.debrisWeather) + current6.draw(Game1.spriteBatch); + } + Game1.farmEvent?.draw(Game1.spriteBatch); + if (Game1.currentLocation.LightLevel > 0f && Game1.timeOfDay < 2000) + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * Game1.currentLocation.LightLevel); + if (Game1.screenGlow) + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Game1.screenGlowColor * Game1.screenGlowAlpha); + Game1.currentLocation.drawAboveAlwaysFrontLayer(Game1.spriteBatch); + if (Game1.player.CurrentTool is FishingRod && ((Game1.player.CurrentTool as FishingRod).isTimingCast || (Game1.player.CurrentTool as FishingRod).castingChosenCountdown > 0f || (Game1.player.CurrentTool as FishingRod).fishCaught || (Game1.player.CurrentTool as FishingRod).showingTreasure)) + Game1.player.CurrentTool.draw(Game1.spriteBatch); + if (Game1.isRaining && Game1.currentLocation.IsOutdoors && !Game1.currentLocation.Name.Equals("Desert") && !(Game1.currentLocation is Summit) && (!Game1.eventUp || Game1.currentLocation.isTileOnMap(new Vector2(Game1.viewport.X / Game1.tileSize, Game1.viewport.Y / Game1.tileSize)))) + { + for (int j = 0; j < Game1.rainDrops.Length; j++) + Game1.spriteBatch.Draw(Game1.rainTexture, Game1.rainDrops[j].position, Game1.getSourceRectForStandardTileSheet(Game1.rainTexture, Game1.rainDrops[j].frame), Color.White); + } + + Game1.spriteBatch.End(); + + //base.Draw(gameTime); + + Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (Game1.eventUp && Game1.currentLocation.currentEvent != null) + { + foreach (NPC current7 in Game1.currentLocation.currentEvent.actors) + { + if (current7.isEmoting) + { + Vector2 localPosition = current7.getLocalPosition(Game1.viewport); + localPosition.Y -= Game1.tileSize * 2 + Game1.pixelZoom * 3; + if (current7.age == 2) + localPosition.Y += Game1.tileSize / 2; + else if (current7.gender == 1) + localPosition.Y += Game1.tileSize / 6; + Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, localPosition, new Rectangle(current7.CurrentEmoteIndex * (Game1.tileSize / 4) % Game1.emoteSpriteSheet.Width, current7.CurrentEmoteIndex * (Game1.tileSize / 4) / Game1.emoteSpriteSheet.Width * (Game1.tileSize / 4), Game1.tileSize / 4, Game1.tileSize / 4), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, current7.getStandingY() / 10000f); + } + } + } + Game1.spriteBatch.End(); + if (Game1.drawLighting) + { + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, new BlendState + { + ColorBlendFunction = BlendFunction.ReverseSubtract, + ColorDestinationBlend = Blend.One, + ColorSourceBlend = Blend.SourceColor + }, SamplerState.LinearClamp, null, null); + Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, Game1.lightmap.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.lightingQuality, SpriteEffects.None, 1f); + if (Game1.isRaining && Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert)) + { + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.OrangeRed * 0.45f); + } + Game1.spriteBatch.End(); + } + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (Game1.drawGrid) + { + int num2 = -Game1.viewport.X % Game1.tileSize; + float num3 = -(float)Game1.viewport.Y % Game1.tileSize; + for (int k = num2; k < Game1.graphics.GraphicsDevice.Viewport.Width; k += Game1.tileSize) + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(k, (int)num3, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f); + for (float num4 = num3; num4 < (float)Game1.graphics.GraphicsDevice.Viewport.Height; num4 += (float)Game1.tileSize) + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(num2, (int)num4, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f); + } + if (Game1.currentBillboard != 0) + this.drawBillboard(); + + GraphicsEvents.InvokeOnPreRenderHudEventNoCheck(this.Monitor); + if ((Game1.displayHUD || Game1.eventUp) && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !Game1.freezeControls && !Game1.panMode) + { + GraphicsEvents.InvokeOnPreRenderHudEvent(this.Monitor); + SGame.DrawHUD.Invoke(Program.gamePtr, null); + GraphicsEvents.InvokeOnPostRenderHudEvent(this.Monitor); + } + else if (Game1.activeClickableMenu == null && Game1.farmEvent == null) + Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2(Game1.getOldMouseX(), Game1.getOldMouseY()), Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, 0, 16, 16), Color.White, 0f, Vector2.Zero, 4f + Game1.dialogueButtonScale / 150f, SpriteEffects.None, 1f); + GraphicsEvents.InvokeOnPostRenderHudEventNoCheck(this.Monitor); + + if (Game1.hudMessages.Any() && (!Game1.eventUp || Game1.isFestival())) + { + for (int l = Game1.hudMessages.Count - 1; l >= 0; l--) + Game1.hudMessages[l].draw(Game1.spriteBatch, l); + } + } + Game1.farmEvent?.draw(Game1.spriteBatch); + if (Game1.dialogueUp && !Game1.nameSelectUp && !Game1.messagePause && !(Game1.activeClickableMenu is DialogueBox)) + SGame.DrawDialogueBox.Invoke(Program.gamePtr, null); + if (Game1.progressBar) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - Game1.tileSize * 2, Game1.dialogueWidth, Game1.tileSize / 2), Color.LightGray); + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - Game1.tileSize * 2, (int)(Game1.pauseAccumulator / Game1.pauseTime * Game1.dialogueWidth), Game1.tileSize / 2), Color.DimGray); + } + if (Game1.eventUp) + Game1.currentLocation.currentEvent?.drawAfterMap(Game1.spriteBatch); + if (Game1.isRaining && Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert)) + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f); + if ((Game1.fadeToBlack || Game1.globalFade) && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause)) + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); + else if (Game1.flashAlpha > 0f) + { + if (Game1.options.screenFlash) + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.White * Math.Min(1f, Game1.flashAlpha)); + Game1.flashAlpha -= 0.1f; + } + if ((Game1.messagePause || Game1.globalFade) && Game1.dialogueUp) + SGame.DrawDialogueBox.Invoke(Program.gamePtr, null); + foreach (TemporaryAnimatedSprite current8 in Game1.screenOverlayTempSprites) + current8.draw(Game1.spriteBatch, true); + if (Game1.debugMode) + { + Game1.spriteBatch.DrawString(Game1.smallFont, string.Concat(new object[] + { + Game1.panMode ? ((Game1.getOldMouseX() + Game1.viewport.X) / Game1.tileSize + "," + (Game1.getOldMouseY() + Game1.viewport.Y) / Game1.tileSize) : string.Concat("aplayer: ", Game1.player.getStandingX() / Game1.tileSize, ", ", Game1.player.getStandingY() / Game1.tileSize), + Environment.NewLine, + "debugOutput: ", + Game1.debugOutput + }), new Vector2(this.GraphicsDevice.Viewport.TitleSafeArea.X, this.GraphicsDevice.Viewport.TitleSafeArea.Y), Color.Red, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); + } + /*if (inputMode) + { + spriteBatch.DrawString(smallFont, "Input: " + debugInput, new Vector2(tileSize, tileSize * 3), Color.Purple); + }*/ + if (Game1.showKeyHelp) + Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(Game1.tileSize, Game1.viewport.Height - Game1.tileSize - (Game1.dialogueUp ? (Game1.tileSize * 3 + (Game1.isQuestion ? (Game1.questionChoices.Count * Game1.tileSize) : 0)) : 0) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); + + GraphicsEvents.InvokeOnPreRenderGuiEventNoCheck(this.Monitor); + if (Game1.activeClickableMenu != null) + { + GraphicsEvents.InvokeOnPreRenderGuiEvent(this.Monitor); + try + { + Game1.activeClickableMenu.draw(Game1.spriteBatch); + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + GraphicsEvents.InvokeOnPostRenderGuiEvent(this.Monitor); + } + else + Game1.farmEvent?.drawAboveEverything(Game1.spriteBatch); + GraphicsEvents.InvokeOnPostRenderGuiEventNoCheck(this.Monitor); + + GraphicsEvents.InvokeOnPostRenderEvent(this.Monitor); + Game1.spriteBatch.End(); + + GraphicsEvents.InvokeDrawInRenderTargetTick(this.Monitor); + + if (!this.ZoomLevelIsOne) + { + this.GraphicsDevice.SetRenderTarget(null); + this.GraphicsDevice.Clear(this.BgColour); + Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + } + + GraphicsEvents.InvokeDrawTick(this.Monitor); + } + catch (Exception ex) + { + this.Monitor.Log($"An error occured in the overridden draw loop: {ex.GetLogSummary()}", LogLevel.Error); + } + + if (SGame.Debug) + { + Game1.spriteBatch.Begin(); + + int i = 0; + while (SGame.DebugMessageQueue.Any()) + { + string message = SGame.DebugMessageQueue.Dequeue(); + Game1.spriteBatch.DrawString(Game1.smoothFont, message, new Vector2(0, i * 14), Color.CornflowerBlue); + i++; + } + GraphicsEvents.InvokeDrawDebug(this.Monitor); + + Game1.spriteBatch.End(); + } + else + SGame.DebugMessageQueue.Clear(); + } + + /// Get whether a controller button was pressed since the last check. + /// The controller button to check. + /// The last known state. + /// The player whose controller to check. + private bool WasButtonJustPressed(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) + { + return buttonState == ButtonState.Pressed && !this.PreviouslyPressedButtons[(int)stateIndex].Contains(button); + } + + /// Get whether a controller button was released since the last check. + /// The controller button to check. + /// The last known state. + /// The player whose controller to check. + private bool WasButtonJustReleased(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) + { + return buttonState == ButtonState.Released && this.PreviouslyPressedButtons[(int)stateIndex].Contains(button); + } + + /// Get whether an analogue controller button was pressed since the last check. + /// The controller button to check. + /// The last known value. + /// The player whose controller to check. + private bool WasButtonJustPressed(Buttons button, float value, PlayerIndex stateIndex) + { + return this.WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); + } + + /// Get whether an analogue controller button was released since the last check. + /// The controller button to check. + /// The last known value. + /// The player whose controller to check. + private bool WasButtonJustReleased(Buttons button, float value, PlayerIndex stateIndex) + { + return this.WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); + } + + /// Detect changes since the last update ticket and trigger mod events. + private void UpdateEventCalls() + { + // save loaded event + if (Game1.hasLoadedGame && this.AfterLoadTimer >= 0) + { + if (this.AfterLoadTimer == 0) + { + SaveEvents.InvokeAfterLoad(this.Monitor); + PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); + } + this.AfterLoadTimer--; + } + + // input events + { + // get latest state + this.KStateNow = Keyboard.GetState(); + this.MStateNow = Mouse.GetState(); + this.MPositionNow = new Point(Game1.getMouseX(), Game1.getMouseY()); + + // raise key pressed + foreach (var key in this.FramePressedKeys) + ControlEvents.InvokeKeyPressed(this.Monitor, key); + + // raise key released + foreach (var key in this.FrameReleasedKeys) + ControlEvents.InvokeKeyReleased(this.Monitor, key); + + // raise controller button pressed + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + var buttons = this.GetFramePressedButtons(i); + foreach (var button in buttons) + { + if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) + ControlEvents.InvokeTriggerPressed(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + else + ControlEvents.InvokeButtonPressed(this.Monitor, i, button); + } + } + + // raise controller button released + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + foreach (var button in this.GetFrameReleasedButtons(i)) + { + if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) + ControlEvents.InvokeTriggerReleased(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + else + ControlEvents.InvokeButtonReleased(this.Monitor, i, button); + } + } + + // raise keyboard state changed + if (this.KStateNow != this.KStatePrior) + ControlEvents.InvokeKeyboardChanged(this.Monitor, this.KStatePrior, this.KStateNow); + + // raise mouse state changed + if (this.MStateNow != this.MStatePrior) + { + ControlEvents.InvokeMouseChanged(this.Monitor, this.MStatePrior, this.MStateNow, this.MPositionPrior, this.MPositionNow); + this.MStatePrior = this.MStateNow; + this.MPositionPrior = this.MPositionPrior; + } + } + + // menu events + if (Game1.activeClickableMenu != this.PreviousActiveMenu) + { + IClickableMenu previousMenu = this.PreviousActiveMenu; + IClickableMenu newMenu = Game1.activeClickableMenu; + + // raise save events + // (saving is performed by SaveGameMenu; on days when the player shipping something, ShippingMenu wraps SaveGameMenu) + if (newMenu is SaveGameMenu || newMenu is ShippingMenu) + SaveEvents.InvokeBeforeSave(this.Monitor); + else if (previousMenu is SaveGameMenu || previousMenu is ShippingMenu) + SaveEvents.InvokeAfterSave(this.Monitor); + + // raise menu events + if (newMenu != null) + MenuEvents.InvokeMenuChanged(this.Monitor, previousMenu, newMenu); + else + MenuEvents.InvokeMenuClosed(this.Monitor, previousMenu); + + // update previous menu + // (if the menu was changed in one of the handlers, deliberately defer detection until the next update so mods can be notified of the new menu change) + this.PreviousActiveMenu = newMenu; + } + + // world & player events + if (this.IsWorldReady) + { + // raise location list changed + if (this.GetHash(Game1.locations) != this.PreviousGameLocations) + { + LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations); + this.PreviousGameLocations = this.GetHash(Game1.locations); + } + + // raise current location changed + if (Game1.currentLocation != this.PreviousGameLocation) + { + LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation); + this.PreviousGameLocation = Game1.currentLocation; + } + + // raise player changed + if (Game1.player != this.PreviousFarmer) + { + PlayerEvents.InvokeFarmerChanged(this.Monitor, this.PreviousFarmer, Game1.player); + this.PreviousFarmer = Game1.player; + } + + // raise player leveled up a skill + if (Game1.player.combatLevel != this.PreviousCombatLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Combat, Game1.player.combatLevel); + this.PreviousCombatLevel = Game1.player.combatLevel; + } + if (Game1.player.farmingLevel != this.PreviousFarmingLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Farming, Game1.player.farmingLevel); + this.PreviousFarmingLevel = Game1.player.farmingLevel; + } + if (Game1.player.fishingLevel != this.PreviousFishingLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Fishing, Game1.player.fishingLevel); + this.PreviousFishingLevel = Game1.player.fishingLevel; + } + if (Game1.player.foragingLevel != this.PreviousForagingLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Foraging, Game1.player.foragingLevel); + this.PreviousForagingLevel = Game1.player.foragingLevel; + } + if (Game1.player.miningLevel != this.PreviousMiningLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Mining, Game1.player.miningLevel); + this.PreviousMiningLevel = Game1.player.miningLevel; + } + if (Game1.player.luckLevel != this.PreviousLuckLevel) + { + PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Luck, Game1.player.luckLevel); + this.PreviousLuckLevel = Game1.player.luckLevel; + } + + // raise player inventory changed + ItemStackChange[] changedItems = this.GetInventoryChanges(Game1.player.items, this.PreviousItems).ToArray(); + if (changedItems.Any()) + { + PlayerEvents.InvokeInventoryChanged(this.Monitor, Game1.player.items, changedItems); + this.PreviousItems = Game1.player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack); + } + + // raise current location's object list changed + { + int? objectHash = Game1.currentLocation?.objects != null ? this.GetHash(Game1.currentLocation.objects) : (int?)null; + if (objectHash != null && this.PreviousLocationObjects != objectHash) + { + LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects); + this.PreviousLocationObjects = objectHash.Value; + } + } + + // raise time changed + if (Game1.timeOfDay != this.PreviousTimeOfDay) + { + TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTimeOfDay, Game1.timeOfDay); + this.PreviousTimeOfDay = Game1.timeOfDay; + } + if (Game1.dayOfMonth != this.PreviousDayOfMonth) + { + TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth); + this.PreviousDayOfMonth = Game1.dayOfMonth; + } + if (Game1.currentSeason != this.PreviousSeasonOfYear) + { + TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeasonOfYear, Game1.currentSeason); + this.PreviousSeasonOfYear = Game1.currentSeason; + } + if (Game1.year != this.PreviousYearOfGame) + { + TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYearOfGame, Game1.year); + this.PreviousYearOfGame = Game1.year; + } + + // raise mine level changed + if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel) + { + MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel); + this.PreviousMineLevel = Game1.mine.mineLevel; + } + } + + // raise game day transition event (obsolete) + if (Game1.newDay != this.PreviousIsNewDay) + { + TimeEvents.InvokeOnNewDay(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth, Game1.newDay); + this.PreviousIsNewDay = Game1.newDay; + } + } + + /// Get the player inventory changes between two states. + /// The player's current inventory. + /// The player's previous inventory. + private IEnumerable GetInventoryChanges(IEnumerable current, IDictionary previous) + { + current = current.Where(n => n != null).ToArray(); + foreach (Item item in current) + { + // stack size changed + if (previous != null && previous.ContainsKey(item)) + { + if (previous[item] != item.Stack) + yield return new ItemStackChange { Item = item, StackChange = item.Stack - previous[item], ChangeType = ChangeType.StackChange }; + } + + // new item + else + yield return new ItemStackChange { Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added }; + } + + // removed items + if (previous != null) + { + foreach (var entry in previous) + { + if (current.Any(i => i == entry.Key)) + continue; + + yield return new ItemStackChange { Item = entry.Key, StackChange = -entry.Key.Stack, ChangeType = ChangeType.Removed }; + } + } + } + + /// Get a hash value for an enumeration. + /// The enumeration of items to hash. + private int GetHash(IEnumerable enumerable) + { + var hash = 0; + foreach (var v in enumerable) + hash ^= v.GetHashCode(); + return hash; + } + + /// Get reflection metadata for a private field. + /// The field name. + private FieldInfo GetBaseFieldInfo(string name) + { + return typeof(Game1).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); + } + + /// Get the value of a private field. + /// The expected value type. + /// The field name. + private TValue GetBaseFieldValue(string name) where TValue : class + { + return this.GetBaseFieldInfo(name).GetValue(Program.gamePtr) as TValue; + } + + /// Set the value of a private field. + /// The expected value type. + /// The field name. + /// The value to set. + public void SetBaseFieldValue(string name, object value) where TValue : class + { + this.GetBaseFieldInfo(name).SetValue(Program.gamePtr, value as TValue); + } + } +} diff --git a/src/StardewModdingAPI/Framework/Serialisation/SemanticVersionConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/SemanticVersionConverter.cs new file mode 100644 index 00000000..52ec999e --- /dev/null +++ b/src/StardewModdingAPI/Framework/Serialisation/SemanticVersionConverter.cs @@ -0,0 +1,51 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI.Framework.Serialisation +{ + /// Overrides how SMAPI reads and writes . + internal class SemanticVersionConverter : JsonConverter + { + /********* + ** Accessors + *********/ + /// Whether this converter can write JSON. + public override bool CanWrite => false; + + + /********* + ** Public methods + *********/ + /// Get whether this instance can convert the specified object type. + /// The object type. + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ISemanticVersion); + } + + /// Reads the JSON representation of the object. + /// The JSON reader. + /// The object type. + /// The object being read. + /// The calling serializer. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject obj = JObject.Load(reader); + int major = obj.Value("MajorVersion"); + int minor = obj.Value("MinorVersion"); + int patch = obj.Value("PatchVersion"); + string build = obj.Value("Build"); + return new SemanticVersion(major, minor, patch, build); + } + + /// Writes the JSON representation of the object. + /// The JSON writer. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new InvalidOperationException("This converter does not write JSON."); + } + } +} diff --git a/src/StardewModdingAPI/Inheritance/ChangeType.cs b/src/StardewModdingAPI/Inheritance/ChangeType.cs deleted file mode 100644 index 94eb33ed..00000000 --- a/src/StardewModdingAPI/Inheritance/ChangeType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace StardewModdingAPI.Inheritance -{ - /// Indicates how an inventory item changed. - public enum ChangeType - { - /// The entire stack was removed. - Removed, - - /// The entire stack was added. - Added, - - /// The stack size changed. - StackChange - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/ItemStackChange.cs b/src/StardewModdingAPI/Inheritance/ItemStackChange.cs deleted file mode 100644 index 8d15b894..00000000 --- a/src/StardewModdingAPI/Inheritance/ItemStackChange.cs +++ /dev/null @@ -1,20 +0,0 @@ -using StardewValley; - -namespace StardewModdingAPI.Inheritance -{ - /// Represents an inventory slot that changed. - public class ItemStackChange - { - /********* - ** Accessors - *********/ - /// The item in the slot. - public Item Item { get; set; } - - /// The amount by which the item's stack size changed. - public int StackChange { get; set; } - - /// How the inventory slot changed. - public ChangeType ChangeType { get; set; } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs deleted file mode 100644 index 69c20244..00000000 --- a/src/StardewModdingAPI/Inheritance/SGame.cs +++ /dev/null @@ -1,1134 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using StardewModdingAPI.Events; -using StardewModdingAPI.Framework; -using StardewValley; -using StardewValley.BellsAndWhistles; -using StardewValley.Locations; -using StardewValley.Menus; -using StardewValley.Tools; -using xTile.Dimensions; -using Rectangle = Microsoft.Xna.Framework.Rectangle; - -namespace StardewModdingAPI.Inheritance -{ - /// SMAPI's extension of the game's core , used to inject events. - public class SGame : Game1 - { - /********* - ** Properties - *********/ - /// The number of ticks until SMAPI should notify mods when is set. - /// Skipping a few frames ensures the game finishes initialising the world before mods try to change it. - private int AfterLoadTimer = 5; - - /// Whether the player has loaded a save and the world has finished initialising. - private bool IsWorldReady => this.AfterLoadTimer < 0; - - /// The debug messages to add to the next debug output. - internal static Queue DebugMessageQueue { get; private set; } - - /// Whether the game's zoom level is at 100% (i.e. nothing should be scaled). - public bool ZoomLevelIsOne => Game1.options.zoomLevel.Equals(1.0f); - - /// Encapsulates monitoring and logging. - private readonly IMonitor Monitor; - - - /********* - ** Accessors - *********/ - /// Arrays of pressed controller buttons indexed by . - public Buttons[][] PreviouslyPressedButtons; - - /// A record of the keyboard state (i.e. the up/down state for each button) as of the latest tick. - public KeyboardState KStateNow { get; private set; } - - /// A record of the keyboard state (i.e. the up/down state for each button) as of the previous tick. - public KeyboardState KStatePrior { get; private set; } - - /// A record of the mouse state (i.e. the cursor position, scroll amount, and the up/down state for each button) as of the latest tick. - public MouseState MStateNow { get; private set; } - - /// 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. - public MouseState MStatePrior { get; private set; } - - /// The current mouse position on the screen adjusted for the zoom level. - public Point MPositionNow { get; private set; } - - /// The previous mouse position on the screen adjusted for the zoom level. - public Point MPositionPrior { get; private set; } - - /// The keys that were pressed as of the latest tick. - public Keys[] CurrentlyPressedKeys => this.KStateNow.GetPressedKeys(); - - /// The keys that were pressed as of the previous tick. - public Keys[] PreviouslyPressedKeys => this.KStatePrior.GetPressedKeys(); - - /// The keys that just entered the down state. - public Keys[] FramePressedKeys => this.CurrentlyPressedKeys.Except(this.PreviouslyPressedKeys).ToArray(); - - /// The keys that just entered the up state. - public Keys[] FrameReleasedKeys => this.PreviouslyPressedKeys.Except(this.CurrentlyPressedKeys).ToArray(); - - /// Whether a save is currently loaded at last check. - public bool PreviouslyLoadedGame { get; private set; } - - /// A hash of at last check. - public int PreviousGameLocations { get; private set; } - - /// A hash of the current location's at last check. - public int PreviousLocationObjects { get; private set; } - - /// The player's inventory at last check. - public Dictionary PreviousItems { get; private set; } - - /// The player's combat skill level at last check. - public int PreviousCombatLevel { get; private set; } - - /// The player's farming skill level at last check. - public int PreviousFarmingLevel { get; private set; } - - /// The player's fishing skill level at last check. - public int PreviousFishingLevel { get; private set; } - - /// The player's foraging skill level at last check. - public int PreviousForagingLevel { get; private set; } - - /// The player's mining skill level at last check. - public int PreviousMiningLevel { get; private set; } - - /// The player's luck skill level at last check. - public int PreviousLuckLevel { get; private set; } - - /// The player's location at last check. - public GameLocation PreviousGameLocation { get; private set; } - - /// The active game menu at last check. - public IClickableMenu PreviousActiveMenu { get; private set; } - - /// The mine level at last check. - public int PreviousMineLevel { get; private set; } - - /// The time of day (in 24-hour military format) at last check. - public int PreviousTimeOfDay { get; private set; } - - /// The day of month (1–28) at last check. - public int PreviousDayOfMonth { get; private set; } - - /// The season name (winter, spring, summer, or fall) at last check. - public string PreviousSeasonOfYear { get; private set; } - - /// The year number at last check. - public int PreviousYearOfGame { get; private set; } - - /// Whether the game was transitioning to a new day at last check. - public bool PreviousIsNewDay { get; private set; } - - /// The player character at last check. - public Farmer PreviousFarmer { get; private set; } - - /// An index incremented on every tick and reset every 60th tick (0–59). - public int CurrentUpdateTick { get; private set; } - - /// Whether this is the very first update tick since the game started. - public bool FirstUpdate { get; private set; } - - /// The game's current render target. - public RenderTarget2D Screen - { - get { return this.GetBaseFieldValue("screen"); } - set { this.SetBaseFieldValue("screen", value); } - } - - /// The game's current background color. - public Color BgColour - { - get { return (Color)this.GetBaseFieldValue("bgColor"); } - set { this.SetBaseFieldValue("bgColor", value); } - } - - /// The current game instance. - public static SGame Instance { get; private set; } - - /// The game's current frame rate, recalculated on each draw update. - public static float FramesPerSecond { get; private set; } - - /// Whether we're in pseudo-debug mode, which shows information like FPS. - public static bool Debug { get; private set; } - - /// The current player. - [Obsolete("Use Game1.player instead")] - public Farmer CurrentFarmer => Game1.player; - - /// The game method which draws the farm buildings. - public static MethodInfo DrawFarmBuildings = typeof(Game1).GetMethod("drawFarmBuildings", BindingFlags.NonPublic | BindingFlags.Instance); - - /// The game method which draws the game HUD. - public static MethodInfo DrawHUD = typeof(Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance); - - /// The game method which draws the current dialogue box, if any. - public static MethodInfo DrawDialogueBox = typeof(Game1).GetMethod("drawDialogueBox", BindingFlags.NonPublic | BindingFlags.Instance); - - - /********* - ** Public methods - *********/ - /// Get the controller buttons which are currently pressed. - /// The controller to check. - public Buttons[] GetButtonsDown(PlayerIndex index) - { - var state = GamePad.GetState(index); - var buttons = new List(); - if (state.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); - } - return buttons.ToArray(); - } - - /// Get the controller buttons which were pressed after the last update. - /// The controller to check. - public Buttons[] GetFramePressedButtons(PlayerIndex index) - { - var state = GamePad.GetState(index); - var buttons = new List(); - if (state.IsConnected) - { - if (this.WasButtonJustPressed(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); - if (this.WasButtonJustPressed(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); - if (this.WasButtonJustPressed(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); - if (this.WasButtonJustPressed(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); - if (this.WasButtonJustPressed(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); - if (this.WasButtonJustPressed(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); - if (this.WasButtonJustPressed(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); - if (this.WasButtonJustPressed(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); - if (this.WasButtonJustPressed(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); - if (this.WasButtonJustPressed(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); - if (this.WasButtonJustPressed(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); - if (this.WasButtonJustPressed(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); - if (this.WasButtonJustPressed(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); - if (this.WasButtonJustPressed(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); - if (this.WasButtonJustPressed(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); - if (this.WasButtonJustPressed(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); - if (this.WasButtonJustPressed(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); - } - return buttons.ToArray(); - } - - /// Get the controller buttons which were released after the last update. - /// The controller to check. - public Buttons[] GetFrameReleasedButtons(PlayerIndex index) - { - var state = GamePad.GetState(index); - var buttons = new List(); - if (state.IsConnected) - { - if (this.WasButtonJustReleased(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); - if (this.WasButtonJustReleased(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); - if (this.WasButtonJustReleased(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); - if (this.WasButtonJustReleased(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); - if (this.WasButtonJustReleased(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); - if (this.WasButtonJustReleased(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); - if (this.WasButtonJustReleased(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); - if (this.WasButtonJustReleased(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); - if (this.WasButtonJustReleased(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); - if (this.WasButtonJustReleased(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); - if (this.WasButtonJustReleased(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); - if (this.WasButtonJustReleased(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); - if (this.WasButtonJustReleased(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); - if (this.WasButtonJustReleased(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); - if (this.WasButtonJustReleased(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); - if (this.WasButtonJustReleased(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); - if (this.WasButtonJustReleased(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); - } - return buttons.ToArray(); - } - - /// Queue a message to be added to the debug output. - /// The message to add. - /// Returns whether the message was successfully queued. - public static bool QueueDebugMessage(string message) - { - if (!SGame.Debug) - return false; - if (SGame.DebugMessageQueue.Count > 32) - return false; - - SGame.DebugMessageQueue.Enqueue(message); - return true; - } - - - /********* - ** Protected methods - *********/ - /// Construct an instance. - /// Encapsulates monitoring and logging. - internal SGame(IMonitor monitor) - { - this.Monitor = monitor; - this.FirstUpdate = true; - SGame.Instance = this; - } - - /// The method called during game launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. - protected override void Initialize() - { - //ModItems = new Dictionary(); - SGame.DebugMessageQueue = new Queue(); - this.PreviouslyPressedButtons = new Buttons[4][]; - for (var i = 0; i < 4; ++i) - this.PreviouslyPressedButtons[i] = new Buttons[0]; - - base.Initialize(); - GameEvents.InvokeInitialize(this.Monitor); - } - - /// The method called before XNA or MonoGame loads or reloads graphics resources. - protected override void LoadContent() - { - base.LoadContent(); - GameEvents.InvokeLoadContent(this.Monitor); - } - - /// The method called when the game is updating its state. This happens roughly 60 times per second. - /// A snapshot of the game timing state. - protected override void Update(GameTime gameTime) - { - // add FPS to debug output - SGame.QueueDebugMessage($"FPS: {SGame.FramesPerSecond}"); - - // raise game loaded - if (this.FirstUpdate) - GameEvents.InvokeGameLoaded(this.Monitor); - - // update SMAPI events - this.UpdateEventCalls(); - - // toggle debug output - if (this.FramePressedKeys.Contains(Keys.F3)) - SGame.Debug = !SGame.Debug; - - // let game update - try - { - base.Update(gameTime); - } - catch (Exception ex) - { - this.Monitor.Log($"An error occured in the base update loop: {ex.GetLogSummary()}", LogLevel.Error); - Console.ReadKey(); - } - - // raise update events - GameEvents.InvokeUpdateTick(this.Monitor); - if (this.FirstUpdate) - { - GameEvents.InvokeFirstUpdateTick(this.Monitor); - this.FirstUpdate = false; - } - if (this.CurrentUpdateTick % 2 == 0) - GameEvents.InvokeSecondUpdateTick(this.Monitor); - if (this.CurrentUpdateTick % 4 == 0) - GameEvents.InvokeFourthUpdateTick(this.Monitor); - if (this.CurrentUpdateTick % 8 == 0) - GameEvents.InvokeEighthUpdateTick(this.Monitor); - if (this.CurrentUpdateTick % 15 == 0) - GameEvents.InvokeQuarterSecondTick(this.Monitor); - if (this.CurrentUpdateTick % 30 == 0) - GameEvents.InvokeHalfSecondTick(this.Monitor); - if (this.CurrentUpdateTick % 60 == 0) - GameEvents.InvokeOneSecondTick(this.Monitor); - this.CurrentUpdateTick += 1; - if (this.CurrentUpdateTick >= 60) - this.CurrentUpdateTick = 0; - - // track keyboard state - if (this.KStatePrior != this.KStateNow) - this.KStatePrior = this.KStateNow; - - // track controller button state - for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) - this.PreviouslyPressedButtons[(int)i] = this.GetButtonsDown(i); - } - - /// The method called to draw everything to the screen. - /// A snapshot of the game timing state. - /// This implementation is identical to , except for try..catch around menu draw code, minor formatting, and added events. - protected override void Draw(GameTime gameTime) - { - // track frame rate - SGame.FramesPerSecond = 1 / (float)gameTime.ElapsedGameTime.TotalSeconds; - - try - { - if (!this.ZoomLevelIsOne) - this.GraphicsDevice.SetRenderTarget(this.Screen); - - this.GraphicsDevice.Clear(this.BgColour); - if (Game1.options.showMenuBackground && Game1.activeClickableMenu != null && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet()) - { - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - try - { - Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing its background. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); - } - GraphicsEvents.InvokeOnPreRenderGuiEvent(this.Monitor); - try - { - Game1.activeClickableMenu.draw(Game1.spriteBatch); - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); - } - GraphicsEvents.InvokeOnPostRenderGuiEvent(this.Monitor); - Game1.spriteBatch.End(); - if (!this.ZoomLevelIsOne) - { - this.GraphicsDevice.SetRenderTarget(null); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); - Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); - Game1.spriteBatch.End(); - } - return; - } - if (Game1.gameMode == 11) - { - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - Game1.spriteBatch.DrawString(Game1.smoothFont, "Stardew Valley has crashed...", new Vector2(16f, 16f), Color.HotPink); - Game1.spriteBatch.DrawString(Game1.smoothFont, "Please send the error report or a screenshot of this message to @ConcernedApe. (http://stardewvalley.net/contact/)", new Vector2(16f, 32f), new Color(0, 255, 0)); - Game1.spriteBatch.DrawString(Game1.smoothFont, Game1.parseText(Game1.errorMessage, Game1.smoothFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White); - Game1.spriteBatch.End(); - return; - } - if (Game1.currentMinigame != null) - { - Game1.currentMinigame.draw(Game1.spriteBatch); - if (Game1.globalFade && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause)) - { - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); - Game1.spriteBatch.End(); - } - if (!this.ZoomLevelIsOne) - { - this.GraphicsDevice.SetRenderTarget(null); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); - Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); - Game1.spriteBatch.End(); - } - return; - } - if (Game1.showingEndOfNightStuff) - { - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - try - { - Game1.activeClickableMenu?.draw(Game1.spriteBatch); - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); - } - Game1.spriteBatch.End(); - if (!this.ZoomLevelIsOne) - { - this.GraphicsDevice.SetRenderTarget(null); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); - Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); - Game1.spriteBatch.End(); - } - return; - } - if (Game1.gameMode == 6) - { - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - string text = ""; - int num = 0; - while (num < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0) - { - text += "."; - num++; - } - SpriteText.drawString(Game1.spriteBatch, "Loading" + text, 64, Game1.graphics.GraphicsDevice.Viewport.Height - 64, 999, -1, 999, 1f, 1f, false, 0, "Loading..."); - Game1.spriteBatch.End(); - if (!this.ZoomLevelIsOne) - { - this.GraphicsDevice.SetRenderTarget(null); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); - Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); - Game1.spriteBatch.End(); - } - return; - } - if (Game1.gameMode == 0) - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - else - { - if (Game1.drawLighting) - { - this.GraphicsDevice.SetRenderTarget(Game1.lightmap); - this.GraphicsDevice.Clear(Color.White * 0f); - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null); - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.lightmap.Bounds, Game1.currentLocation.name.Equals("UndergroundMine") ? Game1.mine.getLightingColor(gameTime) : ((!Game1.ambientLight.Equals(Color.White) && (!Game1.isRaining || !Game1.currentLocation.isOutdoors)) ? Game1.ambientLight : Game1.outdoorLight)); - for (int i = 0; i < Game1.currentLightSources.Count; i++) - { - if (Utility.isOnScreen(Game1.currentLightSources.ElementAt(i).position, (int)(Game1.currentLightSources.ElementAt(i).radius * Game1.tileSize * 4f))) - Game1.spriteBatch.Draw(Game1.currentLightSources.ElementAt(i).lightTexture, Game1.GlobalToLocal(Game1.viewport, Game1.currentLightSources.ElementAt(i).position) / Game1.options.lightingQuality, Game1.currentLightSources.ElementAt(i).lightTexture.Bounds, Game1.currentLightSources.ElementAt(i).color, 0f, new Vector2(Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.X, Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.Y), Game1.currentLightSources.ElementAt(i).radius / Game1.options.lightingQuality, SpriteEffects.None, 0.9f); - } - Game1.spriteBatch.End(); - this.GraphicsDevice.SetRenderTarget(this.ZoomLevelIsOne ? null : this.Screen); - } - if (Game1.bloomDay) - Game1.bloom?.BeginDraw(); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - GraphicsEvents.InvokeOnPreRenderEvent(this.Monitor); - Game1.background?.draw(Game1.spriteBatch); - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - Game1.currentLocation.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); - Game1.currentLocation.drawWater(Game1.spriteBatch); - if (Game1.CurrentEvent == null) - { - using (List.Enumerator enumerator = Game1.currentLocation.characters.GetEnumerator()) - { - while (enumerator.MoveNext()) - { - NPC current = enumerator.Current; - if (current != null && !current.swimming && !current.hideShadow && !current.IsMonster && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current.position + new Vector2(current.sprite.spriteWidth * Game1.pixelZoom / 2f, current.GetBoundingBox().Height + (current.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current.yJumpOffset / 40f) * current.scale, SpriteEffects.None, Math.Max(0f, current.getStandingY() / 10000f) - 1E-06f); - } - goto IL_B30; - } - } - foreach (NPC current2 in Game1.CurrentEvent.actors) - { - if (!current2.swimming && !current2.hideShadow && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current2.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current2.position + new Vector2(current2.sprite.spriteWidth * Game1.pixelZoom / 2f, current2.GetBoundingBox().Height + (current2.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current2.yJumpOffset / 40f) * current2.scale, SpriteEffects.None, Math.Max(0f, current2.getStandingY() / 10000f) - 1E-06f); - } - IL_B30: - if (!Game1.player.swimming && !Game1.player.isRidingHorse() && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(Game1.player.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.player.position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((Game1.player.running || Game1.player.usingTool) && Game1.player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[Game1.player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, 0f); - Game1.currentLocation.Map.GetLayer("Buildings").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); - Game1.mapDisplayDevice.EndScene(); - Game1.spriteBatch.End(); - Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - if (Game1.CurrentEvent == null) - { - using (List.Enumerator enumerator3 = Game1.currentLocation.characters.GetEnumerator()) - { - while (enumerator3.MoveNext()) - { - NPC current3 = enumerator3.Current; - if (current3 != null && !current3.swimming && !current3.hideShadow && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current3.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current3.position + new Vector2(current3.sprite.spriteWidth * Game1.pixelZoom / 2f, current3.GetBoundingBox().Height + (current3.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current3.yJumpOffset / 40f) * current3.scale, SpriteEffects.None, Math.Max(0f, current3.getStandingY() / 10000f) - 1E-06f); - } - goto IL_F5F; - } - } - foreach (NPC current4 in Game1.CurrentEvent.actors) - { - if (!current4.swimming && !current4.hideShadow && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current4.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, current4.position + new Vector2(current4.sprite.spriteWidth * Game1.pixelZoom / 2f, current4.GetBoundingBox().Height + (current4.IsMonster ? 0 : (Game1.pixelZoom * 3)))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), (Game1.pixelZoom + current4.yJumpOffset / 40f) * current4.scale, SpriteEffects.None, Math.Max(0f, current4.getStandingY() / 10000f) - 1E-06f); - } - IL_F5F: - if (!Game1.player.swimming && !Game1.player.isRidingHorse() && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(Game1.player.getTileLocation())) - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.player.position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((Game1.player.running || Game1.player.usingTool) && Game1.player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[Game1.player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, Math.Max(0.0001f, Game1.player.getStandingY() / 10000f + 0.00011f) - 0.0001f); - if (Game1.displayFarmer) - Game1.player.draw(Game1.spriteBatch); - if ((Game1.eventUp || Game1.killScreen) && !Game1.killScreen) - Game1.currentLocation.currentEvent?.draw(Game1.spriteBatch); - if (Game1.player.currentUpgrade != null && Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3 && Game1.currentLocation.Name.Equals("Farm")) - Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), Game1.player.currentUpgrade.getSourceRectangle(), Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + Game1.tileSize * 3 / 4) / 10000f); - Game1.currentLocation.draw(Game1.spriteBatch); - if (Game1.eventUp && Game1.currentLocation.currentEvent?.messageToScreen != null) - Game1.drawWithBorder(Game1.currentLocation.currentEvent.messageToScreen, Color.Black, Color.White, new Vector2(Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width / 2 - Game1.borderFont.MeasureString(Game1.currentLocation.currentEvent.messageToScreen).X / 2f, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Height - Game1.tileSize), 0f, 1f, 0.999f); - if (Game1.player.ActiveObject == null && (Game1.player.UsingTool || Game1.pickingTool) && Game1.player.CurrentTool != null && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool)) - Game1.drawTool(Game1.player); - if (Game1.currentLocation.Name.Equals("Farm")) - SGame.DrawFarmBuildings.Invoke(Program.gamePtr, null); - if (Game1.tvStation >= 0) - Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(6 * Game1.tileSize + Game1.tileSize / 4, 2 * Game1.tileSize + Game1.tileSize / 2)), new Rectangle(Game1.tvStation * 24, 0, 24, 15), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1E-08f); - if (Game1.panMode) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((int)Math.Floor((Game1.getOldMouseX() + Game1.viewport.X) / (double)Game1.tileSize) * Game1.tileSize - Game1.viewport.X, (int)Math.Floor((Game1.getOldMouseY() + Game1.viewport.Y) / (double)Game1.tileSize) * Game1.tileSize - Game1.viewport.Y, Game1.tileSize, Game1.tileSize), Color.Lime * 0.75f); - foreach (Warp current5 in Game1.currentLocation.warps) - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(current5.X * Game1.tileSize - Game1.viewport.X, current5.Y * Game1.tileSize - Game1.viewport.Y, Game1.tileSize, Game1.tileSize), Color.Red * 0.75f); - } - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - Game1.currentLocation.Map.GetLayer("Front").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); - Game1.mapDisplayDevice.EndScene(); - Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch); - Game1.spriteBatch.End(); - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - if (Game1.currentLocation.Name.Equals("Farm") && Game1.stats.SeedsSown >= 200u) - { - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(3 * Game1.tileSize + Game1.tileSize / 4, Game1.tileSize + Game1.tileSize / 3)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize + Game1.tileSize, 2 * Game1.tileSize + Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(5 * Game1.tileSize, 2 * Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(3 * Game1.tileSize + Game1.tileSize / 2, 3 * Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(5 * Game1.tileSize - Game1.tileSize / 4, Game1.tileSize)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize, 3 * Game1.tileSize + Game1.tileSize / 6)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - Game1.spriteBatch.Draw(Game1.debrisSpriteSheet, Game1.GlobalToLocal(Game1.viewport, new Vector2(4 * Game1.tileSize + Game1.tileSize / 5, 2 * Game1.tileSize + Game1.tileSize / 3)), Game1.getSourceRectForStandardTileSheet(Game1.debrisSpriteSheet, 16), Color.White); - } - if (Game1.displayFarmer && Game1.player.ActiveObject != null && Game1.player.ActiveObject.bigCraftable && this.checkBigCraftableBoundariesForFrontLayer() && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null) - Game1.drawPlayerHeldObject(Game1.player); - else if (Game1.displayFarmer && Game1.player.ActiveObject != null && ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.position.X, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.position.X, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")) || (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")))) - Game1.drawPlayerHeldObject(Game1.player); - if ((Game1.player.UsingTool || Game1.pickingTool) && Game1.player.CurrentTool != null && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool) && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), (int)Game1.player.position.Y - Game1.tileSize * 3 / 5), Game1.viewport.Size) != null && Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null) - Game1.drawTool(Game1.player); - if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null) - { - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - Game1.currentLocation.Map.GetLayer("AlwaysFront").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, Game1.pixelZoom); - Game1.mapDisplayDevice.EndScene(); - } - if (Game1.toolHold > 400f && Game1.player.CurrentTool.UpgradeLevel >= 1 && Game1.player.canReleaseTool) - { - Color color = Color.White; - switch ((int)(Game1.toolHold / 600f) + 2) - { - case 1: - color = Tool.copperColor; - break; - case 2: - color = Tool.steelColor; - break; - case 3: - color = Tool.goldColor; - break; - case 4: - color = Tool.iridiumColor; - break; - } - Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X - 2, (int)Game1.player.getLocalPosition(Game1.viewport).Y - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : Game1.tileSize) - 2, (int)(Game1.toolHold % 600f * 0.08f) + 4, Game1.tileSize / 8 + 4), Color.Black); - Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, (int)Game1.player.getLocalPosition(Game1.viewport).Y - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : Game1.tileSize), (int)(Game1.toolHold % 600f * 0.08f), Game1.tileSize / 8), color); - } - if (Game1.isDebrisWeather && Game1.currentLocation.IsOutdoors && !Game1.currentLocation.ignoreDebrisWeather && !Game1.currentLocation.Name.Equals("Desert") && Game1.viewport.X > -10) - { - foreach (WeatherDebris current6 in Game1.debrisWeather) - current6.draw(Game1.spriteBatch); - } - Game1.farmEvent?.draw(Game1.spriteBatch); - if (Game1.currentLocation.LightLevel > 0f && Game1.timeOfDay < 2000) - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * Game1.currentLocation.LightLevel); - if (Game1.screenGlow) - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Game1.screenGlowColor * Game1.screenGlowAlpha); - Game1.currentLocation.drawAboveAlwaysFrontLayer(Game1.spriteBatch); - if (Game1.player.CurrentTool is FishingRod && ((Game1.player.CurrentTool as FishingRod).isTimingCast || (Game1.player.CurrentTool as FishingRod).castingChosenCountdown > 0f || (Game1.player.CurrentTool as FishingRod).fishCaught || (Game1.player.CurrentTool as FishingRod).showingTreasure)) - Game1.player.CurrentTool.draw(Game1.spriteBatch); - if (Game1.isRaining && Game1.currentLocation.IsOutdoors && !Game1.currentLocation.Name.Equals("Desert") && !(Game1.currentLocation is Summit) && (!Game1.eventUp || Game1.currentLocation.isTileOnMap(new Vector2(Game1.viewport.X / Game1.tileSize, Game1.viewport.Y / Game1.tileSize)))) - { - for (int j = 0; j < Game1.rainDrops.Length; j++) - Game1.spriteBatch.Draw(Game1.rainTexture, Game1.rainDrops[j].position, Game1.getSourceRectForStandardTileSheet(Game1.rainTexture, Game1.rainDrops[j].frame), Color.White); - } - - Game1.spriteBatch.End(); - - //base.Draw(gameTime); - - Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - if (Game1.eventUp && Game1.currentLocation.currentEvent != null) - { - foreach (NPC current7 in Game1.currentLocation.currentEvent.actors) - { - if (current7.isEmoting) - { - Vector2 localPosition = current7.getLocalPosition(Game1.viewport); - localPosition.Y -= Game1.tileSize * 2 + Game1.pixelZoom * 3; - if (current7.age == 2) - localPosition.Y += Game1.tileSize / 2; - else if (current7.gender == 1) - localPosition.Y += Game1.tileSize / 6; - Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, localPosition, new Rectangle(current7.CurrentEmoteIndex * (Game1.tileSize / 4) % Game1.emoteSpriteSheet.Width, current7.CurrentEmoteIndex * (Game1.tileSize / 4) / Game1.emoteSpriteSheet.Width * (Game1.tileSize / 4), Game1.tileSize / 4, Game1.tileSize / 4), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, current7.getStandingY() / 10000f); - } - } - } - Game1.spriteBatch.End(); - if (Game1.drawLighting) - { - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, new BlendState - { - ColorBlendFunction = BlendFunction.ReverseSubtract, - ColorDestinationBlend = Blend.One, - ColorSourceBlend = Blend.SourceColor - }, SamplerState.LinearClamp, null, null); - Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, Game1.lightmap.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.lightingQuality, SpriteEffects.None, 1f); - if (Game1.isRaining && Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert)) - { - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.OrangeRed * 0.45f); - } - Game1.spriteBatch.End(); - } - Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); - if (Game1.drawGrid) - { - int num2 = -Game1.viewport.X % Game1.tileSize; - float num3 = -(float)Game1.viewport.Y % Game1.tileSize; - for (int k = num2; k < Game1.graphics.GraphicsDevice.Viewport.Width; k += Game1.tileSize) - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(k, (int)num3, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f); - for (float num4 = num3; num4 < (float)Game1.graphics.GraphicsDevice.Viewport.Height; num4 += (float)Game1.tileSize) - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(num2, (int)num4, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f); - } - if (Game1.currentBillboard != 0) - this.drawBillboard(); - - GraphicsEvents.InvokeOnPreRenderHudEventNoCheck(this.Monitor); - if ((Game1.displayHUD || Game1.eventUp) && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !Game1.freezeControls && !Game1.panMode) - { - GraphicsEvents.InvokeOnPreRenderHudEvent(this.Monitor); - SGame.DrawHUD.Invoke(Program.gamePtr, null); - GraphicsEvents.InvokeOnPostRenderHudEvent(this.Monitor); - } - else if (Game1.activeClickableMenu == null && Game1.farmEvent == null) - Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2(Game1.getOldMouseX(), Game1.getOldMouseY()), Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, 0, 16, 16), Color.White, 0f, Vector2.Zero, 4f + Game1.dialogueButtonScale / 150f, SpriteEffects.None, 1f); - GraphicsEvents.InvokeOnPostRenderHudEventNoCheck(this.Monitor); - - if (Game1.hudMessages.Any() && (!Game1.eventUp || Game1.isFestival())) - { - for (int l = Game1.hudMessages.Count - 1; l >= 0; l--) - Game1.hudMessages[l].draw(Game1.spriteBatch, l); - } - } - Game1.farmEvent?.draw(Game1.spriteBatch); - if (Game1.dialogueUp && !Game1.nameSelectUp && !Game1.messagePause && !(Game1.activeClickableMenu is DialogueBox)) - SGame.DrawDialogueBox.Invoke(Program.gamePtr, null); - if (Game1.progressBar) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - Game1.tileSize * 2, Game1.dialogueWidth, Game1.tileSize / 2), Color.LightGray); - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - Game1.tileSize * 2, (int)(Game1.pauseAccumulator / Game1.pauseTime * Game1.dialogueWidth), Game1.tileSize / 2), Color.DimGray); - } - if (Game1.eventUp) - Game1.currentLocation.currentEvent?.drawAfterMap(Game1.spriteBatch); - if (Game1.isRaining && Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert)) - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f); - if ((Game1.fadeToBlack || Game1.globalFade) && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause)) - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); - else if (Game1.flashAlpha > 0f) - { - if (Game1.options.screenFlash) - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.White * Math.Min(1f, Game1.flashAlpha)); - Game1.flashAlpha -= 0.1f; - } - if ((Game1.messagePause || Game1.globalFade) && Game1.dialogueUp) - SGame.DrawDialogueBox.Invoke(Program.gamePtr, null); - foreach (TemporaryAnimatedSprite current8 in Game1.screenOverlayTempSprites) - current8.draw(Game1.spriteBatch, true); - if (Game1.debugMode) - { - Game1.spriteBatch.DrawString(Game1.smallFont, string.Concat(new object[] - { - Game1.panMode ? ((Game1.getOldMouseX() + Game1.viewport.X) / Game1.tileSize + "," + (Game1.getOldMouseY() + Game1.viewport.Y) / Game1.tileSize) : string.Concat("aplayer: ", Game1.player.getStandingX() / Game1.tileSize, ", ", Game1.player.getStandingY() / Game1.tileSize), - Environment.NewLine, - "debugOutput: ", - Game1.debugOutput - }), new Vector2(this.GraphicsDevice.Viewport.TitleSafeArea.X, this.GraphicsDevice.Viewport.TitleSafeArea.Y), Color.Red, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); - } - /*if (inputMode) - { - spriteBatch.DrawString(smallFont, "Input: " + debugInput, new Vector2(tileSize, tileSize * 3), Color.Purple); - }*/ - if (Game1.showKeyHelp) - Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(Game1.tileSize, Game1.viewport.Height - Game1.tileSize - (Game1.dialogueUp ? (Game1.tileSize * 3 + (Game1.isQuestion ? (Game1.questionChoices.Count * Game1.tileSize) : 0)) : 0) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); - - GraphicsEvents.InvokeOnPreRenderGuiEventNoCheck(this.Monitor); - if (Game1.activeClickableMenu != null) - { - GraphicsEvents.InvokeOnPreRenderGuiEvent(this.Monitor); - try - { - Game1.activeClickableMenu.draw(Game1.spriteBatch); - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); - } - GraphicsEvents.InvokeOnPostRenderGuiEvent(this.Monitor); - } - else - Game1.farmEvent?.drawAboveEverything(Game1.spriteBatch); - GraphicsEvents.InvokeOnPostRenderGuiEventNoCheck(this.Monitor); - - GraphicsEvents.InvokeOnPostRenderEvent(this.Monitor); - Game1.spriteBatch.End(); - - GraphicsEvents.InvokeDrawInRenderTargetTick(this.Monitor); - - if (!this.ZoomLevelIsOne) - { - this.GraphicsDevice.SetRenderTarget(null); - this.GraphicsDevice.Clear(this.BgColour); - Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); - Game1.spriteBatch.Draw(this.Screen, Vector2.Zero, this.Screen.Bounds, Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); - Game1.spriteBatch.End(); - } - - GraphicsEvents.InvokeDrawTick(this.Monitor); - } - catch (Exception ex) - { - this.Monitor.Log($"An error occured in the overridden draw loop: {ex.GetLogSummary()}", LogLevel.Error); - } - - if (SGame.Debug) - { - Game1.spriteBatch.Begin(); - - int i = 0; - while (SGame.DebugMessageQueue.Any()) - { - string message = SGame.DebugMessageQueue.Dequeue(); - Game1.spriteBatch.DrawString(Game1.smoothFont, message, new Vector2(0, i * 14), Color.CornflowerBlue); - i++; - } - GraphicsEvents.InvokeDrawDebug(this.Monitor); - - Game1.spriteBatch.End(); - } - else - SGame.DebugMessageQueue.Clear(); - } - - /// Get whether a controller button was pressed since the last check. - /// The controller button to check. - /// The last known state. - /// The player whose controller to check. - private bool WasButtonJustPressed(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) - { - return buttonState == ButtonState.Pressed && !this.PreviouslyPressedButtons[(int)stateIndex].Contains(button); - } - - /// Get whether a controller button was released since the last check. - /// The controller button to check. - /// The last known state. - /// The player whose controller to check. - private bool WasButtonJustReleased(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) - { - return buttonState == ButtonState.Released && this.PreviouslyPressedButtons[(int)stateIndex].Contains(button); - } - - /// Get whether an analogue controller button was pressed since the last check. - /// The controller button to check. - /// The last known value. - /// The player whose controller to check. - private bool WasButtonJustPressed(Buttons button, float value, PlayerIndex stateIndex) - { - return this.WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); - } - - /// Get whether an analogue controller button was released since the last check. - /// The controller button to check. - /// The last known value. - /// The player whose controller to check. - private bool WasButtonJustReleased(Buttons button, float value, PlayerIndex stateIndex) - { - return this.WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); - } - - /// Detect changes since the last update ticket and trigger mod events. - private void UpdateEventCalls() - { - // save loaded event - if (Game1.hasLoadedGame && this.AfterLoadTimer >= 0) - { - if (this.AfterLoadTimer == 0) - { - SaveEvents.InvokeAfterLoad(this.Monitor); - PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); - } - this.AfterLoadTimer--; - } - - // input events - { - // get latest state - this.KStateNow = Keyboard.GetState(); - this.MStateNow = Mouse.GetState(); - this.MPositionNow = new Point(Game1.getMouseX(), Game1.getMouseY()); - - // raise key pressed - foreach (var key in this.FramePressedKeys) - ControlEvents.InvokeKeyPressed(this.Monitor, key); - - // raise key released - foreach (var key in this.FrameReleasedKeys) - ControlEvents.InvokeKeyReleased(this.Monitor, key); - - // raise controller button pressed - for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) - { - var buttons = this.GetFramePressedButtons(i); - foreach (var button in buttons) - { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) - ControlEvents.InvokeTriggerPressed(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); - else - ControlEvents.InvokeButtonPressed(this.Monitor, i, button); - } - } - - // raise controller button released - for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) - { - foreach (var button in this.GetFrameReleasedButtons(i)) - { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) - ControlEvents.InvokeTriggerReleased(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); - else - ControlEvents.InvokeButtonReleased(this.Monitor, i, button); - } - } - - // raise keyboard state changed - if (this.KStateNow != this.KStatePrior) - ControlEvents.InvokeKeyboardChanged(this.Monitor, this.KStatePrior, this.KStateNow); - - // raise mouse state changed - if (this.MStateNow != this.MStatePrior) - { - ControlEvents.InvokeMouseChanged(this.Monitor, this.MStatePrior, this.MStateNow, this.MPositionPrior, this.MPositionNow); - this.MStatePrior = this.MStateNow; - this.MPositionPrior = this.MPositionPrior; - } - } - - // menu events - if (Game1.activeClickableMenu != this.PreviousActiveMenu) - { - IClickableMenu previousMenu = this.PreviousActiveMenu; - IClickableMenu newMenu = Game1.activeClickableMenu; - - // raise save events - // (saving is performed by SaveGameMenu; on days when the player shipping something, ShippingMenu wraps SaveGameMenu) - if (newMenu is SaveGameMenu || newMenu is ShippingMenu) - SaveEvents.InvokeBeforeSave(this.Monitor); - else if (previousMenu is SaveGameMenu || previousMenu is ShippingMenu) - SaveEvents.InvokeAfterSave(this.Monitor); - - // raise menu events - if (newMenu != null) - MenuEvents.InvokeMenuChanged(this.Monitor, previousMenu, newMenu); - else - MenuEvents.InvokeMenuClosed(this.Monitor, previousMenu); - - // update previous menu - // (if the menu was changed in one of the handlers, deliberately defer detection until the next update so mods can be notified of the new menu change) - this.PreviousActiveMenu = newMenu; - } - - // world & player events - if (this.IsWorldReady) - { - // raise location list changed - if (this.GetHash(Game1.locations) != this.PreviousGameLocations) - { - LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations); - this.PreviousGameLocations = this.GetHash(Game1.locations); - } - - // raise current location changed - if (Game1.currentLocation != this.PreviousGameLocation) - { - LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation); - this.PreviousGameLocation = Game1.currentLocation; - } - - // raise player changed - if (Game1.player != this.PreviousFarmer) - { - PlayerEvents.InvokeFarmerChanged(this.Monitor, this.PreviousFarmer, Game1.player); - this.PreviousFarmer = Game1.player; - } - - // raise player leveled up a skill - if (Game1.player.combatLevel != this.PreviousCombatLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Combat, Game1.player.combatLevel); - this.PreviousCombatLevel = Game1.player.combatLevel; - } - if (Game1.player.farmingLevel != this.PreviousFarmingLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Farming, Game1.player.farmingLevel); - this.PreviousFarmingLevel = Game1.player.farmingLevel; - } - if (Game1.player.fishingLevel != this.PreviousFishingLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Fishing, Game1.player.fishingLevel); - this.PreviousFishingLevel = Game1.player.fishingLevel; - } - if (Game1.player.foragingLevel != this.PreviousForagingLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Foraging, Game1.player.foragingLevel); - this.PreviousForagingLevel = Game1.player.foragingLevel; - } - if (Game1.player.miningLevel != this.PreviousMiningLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Mining, Game1.player.miningLevel); - this.PreviousMiningLevel = Game1.player.miningLevel; - } - if (Game1.player.luckLevel != this.PreviousLuckLevel) - { - PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Luck, Game1.player.luckLevel); - this.PreviousLuckLevel = Game1.player.luckLevel; - } - - // raise player inventory changed - ItemStackChange[] changedItems = this.GetInventoryChanges(Game1.player.items, this.PreviousItems).ToArray(); - if (changedItems.Any()) - { - PlayerEvents.InvokeInventoryChanged(this.Monitor, Game1.player.items, changedItems); - this.PreviousItems = Game1.player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack); - } - - // raise current location's object list changed - { - int? objectHash = Game1.currentLocation?.objects != null ? this.GetHash(Game1.currentLocation.objects) : (int?)null; - if (objectHash != null && this.PreviousLocationObjects != objectHash) - { - LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects); - this.PreviousLocationObjects = objectHash.Value; - } - } - - // raise time changed - if (Game1.timeOfDay != this.PreviousTimeOfDay) - { - TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTimeOfDay, Game1.timeOfDay); - this.PreviousTimeOfDay = Game1.timeOfDay; - } - if (Game1.dayOfMonth != this.PreviousDayOfMonth) - { - TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth); - this.PreviousDayOfMonth = Game1.dayOfMonth; - } - if (Game1.currentSeason != this.PreviousSeasonOfYear) - { - TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeasonOfYear, Game1.currentSeason); - this.PreviousSeasonOfYear = Game1.currentSeason; - } - if (Game1.year != this.PreviousYearOfGame) - { - TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYearOfGame, Game1.year); - this.PreviousYearOfGame = Game1.year; - } - - // raise mine level changed - if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel) - { - MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel); - this.PreviousMineLevel = Game1.mine.mineLevel; - } - } - - // raise game day transition event (obsolete) - if (Game1.newDay != this.PreviousIsNewDay) - { - TimeEvents.InvokeOnNewDay(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth, Game1.newDay); - this.PreviousIsNewDay = Game1.newDay; - } - } - - /// Get the player inventory changes between two states. - /// The player's current inventory. - /// The player's previous inventory. - private IEnumerable GetInventoryChanges(IEnumerable current, IDictionary previous) - { - current = current.Where(n => n != null).ToArray(); - foreach (Item item in current) - { - // stack size changed - if (previous != null && previous.ContainsKey(item)) - { - if (previous[item] != item.Stack) - yield return new ItemStackChange { Item = item, StackChange = item.Stack - previous[item], ChangeType = ChangeType.StackChange }; - } - - // new item - else - yield return new ItemStackChange { Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added }; - } - - // removed items - if (previous != null) - { - foreach (var entry in previous) - { - if (current.Any(i => i == entry.Key)) - continue; - - yield return new ItemStackChange { Item = entry.Key, StackChange = -entry.Key.Stack, ChangeType = ChangeType.Removed }; - } - } - } - - /// Get a hash value for an enumeration. - /// The enumeration of items to hash. - private int GetHash(IEnumerable enumerable) - { - var hash = 0; - foreach (var v in enumerable) - hash ^= v.GetHashCode(); - return hash; - } - - /// Get reflection metadata for a private field. - /// The field name. - private FieldInfo GetBaseFieldInfo(string name) - { - return typeof(Game1).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); - } - - /// Get the value of a private field. - /// The expected value type. - /// The field name. - private TValue GetBaseFieldValue(string name) where TValue : class - { - return this.GetBaseFieldInfo(name).GetValue(Program.gamePtr) as TValue; - } - - /// Set the value of a private field. - /// The expected value type. - /// The field name. - /// The value to set. - public void SetBaseFieldValue(string name, object value) where TValue : class - { - this.GetBaseFieldInfo(name).SetValue(Program.gamePtr, value as TValue); - } - } -} diff --git a/src/StardewModdingAPI/Inheritance/SObject.cs b/src/StardewModdingAPI/Inheritance/SObject.cs deleted file mode 100644 index 0b0a7ec9..00000000 --- a/src/StardewModdingAPI/Inheritance/SObject.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; -using System.Xml.Serialization; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using StardewModdingAPI.Framework; -using StardewValley; -using Object = StardewValley.Object; - -#pragma warning disable 1591 -namespace StardewModdingAPI.Inheritance -{ - /// Provides access to the game's internals. - [Obsolete("This class is deprecated and will be removed in a future version.")] - public class SObject : Object - { - /********* - ** Accessors - *********/ - public string Description { get; set; } - public Texture2D Texture { get; set; } - public string CategoryName { get; set; } - public Color CategoryColour { get; set; } - public bool IsPassable { get; set; } - public bool IsPlaceable { get; set; } - public bool HasBeenRegistered { get; set; } - public int RegisteredId { get; set; } - - public int MaxStackSize { get; set; } - - public bool WallMounted { get; set; } - public Vector2 DrawPosition { get; set; } - - public bool FlaggedForPickup { get; set; } - - [XmlIgnore] - public Vector2 CurrentMouse { get; protected set; } - - [XmlIgnore] - public Vector2 PlacedAt { get; protected set; } - - public override int Stack - { - get { return this.stack; } - set { this.stack = value; } - } - - /********* - ** Public methods - *********/ - public SObject() - { - Program.DeprecationManager.Warn(nameof(SObject), "0.39.3", DeprecationLevel.PendingRemoval); - - this.name = "Modded Item Name"; - this.Description = "Modded Item Description"; - this.CategoryName = "Modded Item Category"; - this.Category = 4163; - this.CategoryColour = Color.White; - this.IsPassable = false; - this.IsPlaceable = false; - this.boundingBox = new Rectangle(0, 0, 64, 64); - this.MaxStackSize = 999; - - this.type = "interactive"; - } - - public override string Name - { - get { return this.name; } - set { this.name = value; } - } - - public override string getDescription() - { - return this.Description; - } - - public override void draw(SpriteBatch spriteBatch, int x, int y, float alpha = 1) - { - if (this.Texture != null) - { - spriteBatch.Draw(this.Texture, Game1.GlobalToLocal(Game1.viewport, new Vector2(x * Game1.tileSize + Game1.tileSize / 2 + (this.shakeTimer > 0 ? Game1.random.Next(-1, 2) : 0), y * Game1.tileSize + Game1.tileSize / 2 + (this.shakeTimer > 0 ? Game1.random.Next(-1, 2) : 0))), Game1.currentLocation.getSourceRectForObject(this.ParentSheetIndex), Color.White * alpha, 0f, new Vector2(8f, 8f), this.scale.Y > 1f ? this.getScale().Y : Game1.pixelZoom, this.flipped ? SpriteEffects.FlipHorizontally : SpriteEffects.None, (this.isPassable() ? this.getBoundingBox(new Vector2(x, y)).Top : this.getBoundingBox(new Vector2(x, y)).Bottom) / 10000f); - } - } - - public new void drawAsProp(SpriteBatch b) - { - } - - public override void drawInMenu(SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, bool drawStackNumber) - { - if (this.isRecipe) - { - transparency = 0.5f; - scaleSize *= 0.75f; - } - - if (this.Texture != null) - { - var targSize = (int) (64 * scaleSize * 0.9f); - var midX = (int) (location.X + 32); - var midY = (int) (location.Y + 32); - - var targX = midX - targSize / 2; - var targY = midY - targSize / 2; - - spriteBatch.Draw(this.Texture, new Rectangle(targX, targY, targSize, targSize), null, new Color(255, 255, 255, transparency), 0, Vector2.Zero, SpriteEffects.None, layerDepth); - } - if (drawStackNumber) - { - var _scale = 0.5f + scaleSize; - Game1.drawWithBorder(this.stack.ToString(), Color.Black, Color.White, location + new Vector2(Game1.tileSize - Game1.tinyFont.MeasureString(string.Concat(this.stack.ToString())).X * _scale, Game1.tileSize - (float) ((double) Game1.tinyFont.MeasureString(string.Concat(this.stack.ToString())).Y * 3.0f / 4.0f) * _scale), 0.0f, _scale, 1f, true); - } - } - - public override void drawWhenHeld(SpriteBatch spriteBatch, Vector2 objectPosition, Farmer f) - { - if (this.Texture != null) - { - var targSize = 64; - var midX = (int) (objectPosition.X + 32); - var midY = (int) (objectPosition.Y + 32); - - var targX = midX - targSize / 2; - var targY = midY - targSize / 2; - - spriteBatch.Draw(this.Texture, new Rectangle(targX, targY, targSize, targSize), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, (f.getStandingY() + 2) / 10000f); - } - } - - public override Color getCategoryColor() - { - return this.CategoryColour; - } - - public override string getCategoryName() - { - if (string.IsNullOrEmpty(this.CategoryName)) - return "Modded Item"; - return this.CategoryName; - } - - public override bool isPassable() - { - return this.IsPassable; - } - - public override bool isPlaceable() - { - return this.IsPlaceable; - } - - public override int maximumStackSize() - { - return this.MaxStackSize; - } - - public SObject Clone() - { - var toRet = new SObject - { - Name = this.Name, - CategoryName = this.CategoryName, - Description = this.Description, - Texture = this.Texture, - IsPassable = this.IsPassable, - IsPlaceable = this.IsPlaceable, - quality = this.quality, - scale = this.scale, - isSpawnedObject = this.isSpawnedObject, - isRecipe = this.isRecipe, - questItem = this.questItem, - stack = 1, - HasBeenRegistered = this.HasBeenRegistered, - RegisteredId = this.RegisteredId - }; - - - return toRet; - } - - public override Item getOne() - { - return this.Clone(); - } - - public override void actionWhenBeingHeld(Farmer who) - { - var x = Game1.oldMouseState.X + Game1.viewport.X; - var y = Game1.oldMouseState.Y + Game1.viewport.Y; - - x = x / Game1.tileSize; - y = y / Game1.tileSize; - - this.CurrentMouse = new Vector2(x, y); - //Program.LogDebug(canBePlacedHere(Game1.currentLocation, CurrentMouse)); - base.actionWhenBeingHeld(who); - } - - public override bool canBePlacedHere(GameLocation l, Vector2 tile) - { - //Program.LogDebug(CurrentMouse.ToString().Replace("{", "").Replace("}", "")); - if (!l.objects.ContainsKey(tile)) - return true; - - return false; - } - - public override bool placementAction(GameLocation location, int x, int y, Farmer who = null) - { - if (Game1.didPlayerJustRightClick()) - return false; - - x = x / Game1.tileSize; - y = y / Game1.tileSize; - - var key = new Vector2(x, y); - - if (!this.canBePlacedHere(location, key)) - return false; - - var s = this.Clone(); - - s.PlacedAt = key; - s.boundingBox = new Rectangle(x / Game1.tileSize * Game1.tileSize, y / Game1.tileSize * Game1.tileSize, this.boundingBox.Width, this.boundingBox.Height); - - location.objects.Add(key, s); - - return true; - } - - public override void actionOnPlayerEntry() - { - //base.actionOnPlayerEntry(); - } - - public override void drawPlacementBounds(SpriteBatch spriteBatch, GameLocation location) - { - if (this.canBePlacedHere(location, this.CurrentMouse)) - { - var targSize = Game1.tileSize; - - var x = Game1.oldMouseState.X + Game1.viewport.X; - var y = Game1.oldMouseState.Y + Game1.viewport.Y; - spriteBatch.Draw(Game1.mouseCursors, new Vector2(x / Game1.tileSize * Game1.tileSize - Game1.viewport.X, y / Game1.tileSize * Game1.tileSize - Game1.viewport.Y), new Rectangle(Utility.playerCanPlaceItemHere(location, this, x, y, Game1.player) ? 194 : 210, 388, 16, 16), Color.White, 0.0f, Vector2.Zero, Game1.pixelZoom, SpriteEffects.None, 0.01f); - } - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/LogWriter.cs b/src/StardewModdingAPI/LogWriter.cs deleted file mode 100644 index e22759a7..00000000 --- a/src/StardewModdingAPI/LogWriter.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using StardewModdingAPI.Framework; - -namespace StardewModdingAPI -{ - /// A log writer which queues messages for output, and periodically flushes them to the console and log file. - /// Only one instance should be created. - [Obsolete("This class is internal and should not be referenced outside SMAPI. It will no longer be exposed in a future version.")] - public class LogWriter - { - /********* - ** Properties - *********/ - /// Manages reading and writing to the log file. - private readonly LogFileManager LogFile; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// Manages reading and writing to the log file. - internal LogWriter(LogFileManager logFile) - { - this.WarnDeprecated(); - this.LogFile = logFile; - } - - /// Queue a message for output. - /// The message to log. - public void WriteToLog(string message) - { - this.WarnDeprecated(); - this.WriteToLog(new LogInfo(message)); - } - - /// Queue a message for output. - /// The message to log. - public void WriteToLog(LogInfo message) - { - this.WarnDeprecated(); - string output = $"[{message.LogTime}] {message.Message}"; - if (message.PrintConsole) - { - if (Monitor.ConsoleSupportsColor) - { - Console.ForegroundColor = message.Colour; - Console.WriteLine(message); - Console.ResetColor(); - } - else - Console.WriteLine(message); - } - this.LogFile.WriteLine(output); - } - - /********* - ** Private methods - *********/ - /// Raise a deprecation warning. - private void WarnDeprecated() - { - Program.DeprecationManager.Warn($"the {nameof(LogWriter)} class", "1.0", DeprecationLevel.PendingRemoval); - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI/Manifest.cs b/src/StardewModdingAPI/Manifest.cs index 07dd3541..baacf954 100644 --- a/src/StardewModdingAPI/Manifest.cs +++ b/src/StardewModdingAPI/Manifest.cs @@ -1,23 +1,11 @@ using System; using Newtonsoft.Json; +using StardewModdingAPI.Framework.Serialisation; namespace StardewModdingAPI { - /// Wraps so it can implement without breaking backwards compatibility. - [Obsolete("Use " + nameof(IManifest) + " or " + nameof(Mod) + "." + nameof(Mod.ModManifest) + " instead")] - internal class ManifestImpl : Manifest, IManifest - { - /// The mod version. - [JsonProperty("Version", ObjectCreationHandling = ObjectCreationHandling.Auto/* avoids issue where Json.NET can't determine concrete type for interface */)] - public new ISemanticVersion Version - { - get { return base.Version; } - set { base.Version = (Version)value; } - } - } - /// A manifest which describes a mod for SMAPI. - public class Manifest + public class Manifest : IManifest { /********* ** Accessors @@ -32,7 +20,8 @@ namespace StardewModdingAPI public string Author { get; set; } /// The mod version. - public Version Version { get; set; } = new Version(0, 0, 0, "", suppressDeprecationWarning: true); + [JsonConverter(typeof(SemanticVersionConverter))] + public ISemanticVersion Version { get; set; } /// The minimum SMAPI version required by this mod, if any. public string MinimumApiVersion { get; set; } @@ -43,26 +32,6 @@ namespace StardewModdingAPI /// The unique mod ID. public string UniqueID { get; set; } - - /**** - ** Obsolete - ****/ - /// Whether the manifest defined the deprecated field. - [JsonIgnore] - internal bool UsedAuthourField { get; private set; } - - /// Obsolete. - [Obsolete("Use " + nameof(Manifest) + "." + nameof(Manifest.Author) + ".")] - public virtual string Authour - { - get { return this.Author; } - set - { - this.UsedAuthourField = true; - this.Author = value; - } - } - /// Whether the mod uses per-save config files. [Obsolete("Use " + nameof(Mod) + "." + nameof(Mod.Helper) + "." + nameof(IModHelper.ReadConfig) + " instead")] public bool PerSaveConfigs { get; set; } diff --git a/src/StardewModdingAPI/Mod.cs b/src/StardewModdingAPI/Mod.cs index 0d35939d..45534e16 100644 --- a/src/StardewModdingAPI/Mod.cs +++ b/src/StardewModdingAPI/Mod.cs @@ -23,17 +23,6 @@ namespace StardewModdingAPI /// Writes messages to the console and log file. public IMonitor Monitor { get; internal set; } - /// The mod's manifest. - [Obsolete("Use " + nameof(Mod) + "." + nameof(ModManifest))] - public Manifest Manifest - { - get - { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(Manifest)}", "1.5", DeprecationLevel.Notice); - return (Manifest)this.ModManifest; - } - } - /// The mod's manifest. public IManifest ModManifest { get; internal set; } @@ -85,11 +74,6 @@ namespace StardewModdingAPI [Obsolete("This overload is obsolete since SMAPI 1.0.")] public virtual void Entry(params object[] objects) { } - /// The mod entry point, called after the mod is first loaded. - /// Provides simplified APIs for writing mods. - [Obsolete("This overload is obsolete since SMAPI 1.1.")] - public virtual void Entry(ModHelper helper) { } - /// The mod entry point, called after the mod is first loaded. /// Provides simplified APIs for writing mods. public virtual void Entry(IModHelper helper) { } @@ -105,7 +89,7 @@ namespace StardewModdingAPI Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(Mod.PerSaveConfigFolder)}", "1.0", DeprecationLevel.Notice); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(Mod.PathOnDisk)}", "1.0"); // avoid redundant warnings - if (!((Manifest)this.Manifest).PerSaveConfigs) + if (!((Manifest)this.ModManifest).PerSaveConfigs) { this.Monitor.Log("Tried to fetch the per-save config folder, but this mod isn't configured to use per-save config files.", LogLevel.Error); return ""; diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 45bf1238..c0a05e2d 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -14,14 +14,13 @@ using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Models; -using StardewModdingAPI.Inheritance; using StardewValley; using Monitor = StardewModdingAPI.Framework.Monitor; namespace StardewModdingAPI { /// The main entry point for SMAPI, responsible for hooking into and launching the game. - public class Program + internal class Program { /********* ** Properties @@ -52,31 +51,27 @@ namespace StardewModdingAPI /// Tracks whether the game should exit immediately and any pending initialisation should be cancelled. private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); - - /********* - ** Accessors - *********/ - /// The number of mods currently loaded by SMAPI. - public static int ModsLoaded; - - /// The underlying game instance. - public static SGame gamePtr; - /// Whether the game is currently running. - public static bool ready; + private static bool ready; /// The underlying game assembly. - public static Assembly StardewAssembly; + private static Assembly StardewAssembly; /// The underlying type. - public static Type StardewProgramType; + private static Type StardewProgramType; /// The field containing game's main instance. - public static FieldInfo StardewGameInfo; + private static FieldInfo StardewGameInfo; - // ReSharper disable once PossibleNullReferenceException - /// The game's build type (i.e. GOG vs Steam). - public static int BuildType => (int)Program.StardewProgramType.GetField("buildType", BindingFlags.Public | BindingFlags.Static).GetValue(null); + + /********* + ** Accessors + *********/ + /// The underlying game instance. + internal static SGame gamePtr; + + /// The number of mods currently loaded by SMAPI. + internal static int ModsLoaded; /// Tracks the installed mods. internal static readonly ModRegistry ModRegistry = new ModRegistry(); @@ -358,7 +353,7 @@ namespace StardewModdingAPI string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'"; // read manifest - ManifestImpl manifest; + Manifest manifest; try { // read manifest text @@ -370,7 +365,7 @@ namespace StardewModdingAPI } // deserialise manifest - manifest = helper.ReadJsonFile("manifest.json"); + manifest = helper.ReadJsonFile("manifest.json"); if (manifest == null) { Program.Monitor.Log($"{errorPrefix}: the manifest file does not exist.", LogLevel.Error); @@ -381,10 +376,6 @@ namespace StardewModdingAPI Program.Monitor.Log($"{errorPrefix}: manifest doesn't specify an entry DLL.", LogLevel.Error); continue; } - - // log deprecated fields - if (manifest.UsedAuthourField) - deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0", DeprecationLevel.Notice)); } catch (Exception ex) { @@ -424,7 +415,7 @@ namespace StardewModdingAPI } catch (FormatException ex) when (ex.Message.Contains("not a valid semantic version")) { - Program.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.Version}.", LogLevel.Error); + Program.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.ApiVersion}.", LogLevel.Error); continue; } } @@ -528,14 +519,11 @@ namespace StardewModdingAPI { // call entry methods mod.Entry(); // deprecated since 1.0 - mod.Entry((ModHelper)mod.Helper); // deprecated since 1.1 mod.Entry(mod.Helper); // raise deprecation warning for old Entry() methods if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.Notice); - if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(ModHelper) })) - Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.PendingRemoval); } catch (Exception ex) { diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index c29f2cf7..3cb592e2 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -13,28 +13,21 @@ namespace StardewModdingAPI /// Derived from https://github.com/maxhauser/semver. private static readonly Regex Regex = new Regex(@"^(?\d+)(\.(?\d+))?(\.(?\d+))?(?.*)$", RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture); - /// The backing field for . - private string _build; - /********* ** Accessors *********/ /// The major version incremented for major API changes. - public int MajorVersion { get; set; } + public int MajorVersion { get; } /// The minor version incremented for backwards-compatible changes. - public int MinorVersion { get; set; } + public int MinorVersion { get; } /// The patch version for backwards-compatible bug fixes. - public int PatchVersion { get; set; } + public int PatchVersion { get; } /// An optional build tag. - public string Build - { - get { return this._build; } - set { this._build = this.GetNormalisedTag(value); } - } + public string Build { get; } /********* @@ -50,7 +43,7 @@ namespace StardewModdingAPI this.MajorVersion = major; this.MinorVersion = minor; this.PatchVersion = patch; - this.Build = build; + this.Build = this.GetNormalisedTag(build); } /// Construct an instance. @@ -65,7 +58,7 @@ namespace StardewModdingAPI this.MajorVersion = int.Parse(match.Groups["major"].Value); this.MinorVersion = match.Groups["minor"].Success ? int.Parse(match.Groups["minor"].Value) : 0; this.PatchVersion = match.Groups["patch"].Success ? int.Parse(match.Groups["patch"].Value) : 0; - this.Build = match.Groups["build"].Success ? match.Groups["build"].Value : null; + this.Build = match.Groups["build"].Success ? this.GetNormalisedTag(match.Groups["build"].Value) : null; } /// Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version. diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 337929e2..aa8cad34 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -123,7 +123,6 @@ - @@ -150,6 +149,7 @@ + @@ -157,7 +157,6 @@ - @@ -178,24 +177,21 @@ - - - + + - - + - diff --git a/src/StardewModdingAPI/Version.cs b/src/StardewModdingAPI/Version.cs deleted file mode 100644 index e66d7be5..00000000 --- a/src/StardewModdingAPI/Version.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using Newtonsoft.Json; -using StardewModdingAPI.Framework; - -namespace StardewModdingAPI -{ - /// A semantic version with an optional release tag. - [Obsolete("Use " + nameof(SemanticVersion) + " or " + nameof(Manifest) + "." + nameof(Manifest.Version) + " instead")] - public struct Version : ISemanticVersion - { - /********* - ** Accessors - *********/ - /// The major version incremented for major API changes. - public int MajorVersion { get; set; } - - /// The minor version incremented for backwards-compatible changes. - public int MinorVersion { get; set; } - - /// The patch version for backwards-compatible bug fixes. - public int PatchVersion { get; set; } - - /// An optional build tag. - public string Build { get; set; } - - /// Obsolete. - [JsonIgnore] - [Obsolete("Use " + nameof(Version) + "." + nameof(Version.ToString) + " instead.")] - public string VersionString - { - get - { - Program.DeprecationManager.Warn($"{nameof(Version)}.{nameof(Version.VersionString)}", "1.0", DeprecationLevel.Info); - return this.GetSemanticVersion().ToString(); - } - } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The major version incremented for major API changes. - /// The minor version incremented for backwards-compatible changes. - /// The patch version for backwards-compatible bug fixes. - /// An optional build tag. - public Version(int major, int minor, int patch, string build) - : this(major, minor, patch, build, suppressDeprecationWarning: false) - { } - - /// Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version. - /// The version to compare with this instance. - public int CompareTo(Version other) - { - return this.GetSemanticVersion().CompareTo(other); - } - - /// Get whether this version is newer than the specified version. - /// The version to compare with this instance. - [Obsolete("Use " + nameof(ISemanticVersion) + "." + nameof(ISemanticVersion.IsNewerThan) + " instead")] - public bool IsNewerThan(Version other) - { - return this.GetSemanticVersion().IsNewerThan(other); - } - - /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes in the sort order. Zero This instance occurs in the same position in the sort order as . Greater than zero This instance follows in the sort order. - /// An object to compare with this instance. - int IComparable.CompareTo(ISemanticVersion other) - { - return this.GetSemanticVersion().CompareTo(other); - } - - /// Get whether this version is older than the specified version. - /// The version to compare with this instance. - bool ISemanticVersion.IsOlderThan(ISemanticVersion other) - { - return this.GetSemanticVersion().IsOlderThan(other); - } - - /// Get whether this version is newer than the specified version. - /// The version to compare with this instance. - bool ISemanticVersion.IsNewerThan(ISemanticVersion other) - { - return this.GetSemanticVersion().IsNewerThan(other); - } - - /// Get a string representation of the version. - public override string ToString() - { - return this.GetSemanticVersion().ToString(); - } - - /********* - ** Private methods - *********/ - /// Construct an instance. - /// The major version incremented for major API changes. - /// The minor version incremented for backwards-compatible changes. - /// The patch version for backwards-compatible bug fixes. - /// An optional build tag. - /// Whether to suppress the deprecation warning. - internal Version(int major, int minor, int patch, string build, bool suppressDeprecationWarning) - { - if (!suppressDeprecationWarning) - Program.DeprecationManager.Warn($"{nameof(Version)}", "1.5", DeprecationLevel.Notice); - - this.MajorVersion = major; - this.MinorVersion = minor; - this.PatchVersion = patch; - this.Build = build; - } - - /// Get the equivalent semantic version. - /// This is a hack so the struct can wrap without a mutable backing field, which would cause a due to recreating the struct value on each change. - private SemanticVersion GetSemanticVersion() - { - return new SemanticVersion(this.MajorVersion, this.MinorVersion, this.PatchVersion, this.Build); - } - } -} diff --git a/src/TrainerMod/TrainerMod.cs b/src/TrainerMod/TrainerMod.cs index c76bb78c..a7cedb6a 100644 --- a/src/TrainerMod/TrainerMod.cs +++ b/src/TrainerMod/TrainerMod.cs @@ -81,9 +81,6 @@ namespace TrainerMod Command.RegisterCommand("save", "Saves the game? Doesn't seem to work. | save").CommandFired += this.HandleSave; Command.RegisterCommand("load", "Shows the load screen | load").CommandFired += this.HandleLoad; - Command.RegisterCommand("exit", "Closes the game | exit").CommandFired += this.HandleExit; - Command.RegisterCommand("stop", "Closes the game | stop").CommandFired += this.HandleExit; - Command.RegisterCommand("player_setname", "Sets the player's name | player_setname ", new[] { "(player, pet, farm) (String) The target name" }).CommandFired += this.HandlePlayerSetName; Command.RegisterCommand("player_setmoney", "Sets the player's money | player_setmoney |inf", new[] { "(Int32) The target money" }).CommandFired += this.HandlePlayerSetMoney; Command.RegisterCommand("player_setstamina", "Sets the player's stamina | player_setstamina |inf", new[] { "(Int32) The target stamina" }).CommandFired += this.HandlePlayerSetStamina; @@ -142,15 +139,6 @@ namespace TrainerMod Game1.activeClickableMenu = new LoadGameMenu(); } - /// The event raised when the 'exit' command is triggered. - /// The event sender. - /// The event arguments. - private void HandleExit(object sender, EventArgsCommand e) - { - Program.gamePtr.Exit(); - Environment.Exit(0); - } - /// The event raised when the 'player_setName' command is triggered. /// The event sender. /// The event arguments. -- cgit