summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs4
-rw-r--r--src/SMAPI.ModBuildConfig/build/smapi.targets1
-rw-r--r--src/SMAPI.ModBuildConfig/package.nuspec2
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs3
-rw-r--r--src/SMAPI/Framework/Input/InputState.cs220
-rw-r--r--src/SMAPI/Framework/Input/SInputState.cs359
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs9
-rw-r--r--src/SMAPI/Framework/Monitor.cs32
-rw-r--r--src/SMAPI/Framework/SGame.cs113
-rw-r--r--src/SMAPI/Program.cs41
-rw-r--r--src/SMAPI/SModHooks.cs48
-rw-r--r--src/SMAPI/StardewModdingAPI.csproj3
12 files changed, 426 insertions, 409 deletions
diff --git a/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs b/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs
index 96bd29f4..d6f8dd7f 100644
--- a/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs
+++ b/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs
@@ -2,5 +2,5 @@ using System.Reflection;
[assembly: AssemblyTitle("SMAPI.ModBuildConfig")]
[assembly: AssemblyDescription("")]
-[assembly: AssemblyVersion("2.0.2.0")]
-[assembly: AssemblyFileVersion("2.0.2.0")]
+[assembly: AssemblyVersion("2.1.0.0")]
+[assembly: AssemblyFileVersion("2.1.0.0")]
diff --git a/src/SMAPI.ModBuildConfig/build/smapi.targets b/src/SMAPI.ModBuildConfig/build/smapi.targets
index 78afa7da..d33a9637 100644
--- a/src/SMAPI.ModBuildConfig/build/smapi.targets
+++ b/src/SMAPI.ModBuildConfig/build/smapi.targets
@@ -45,6 +45,7 @@
<When Condition="$(OS) == 'Windows_NT'">
<PropertyGroup>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
+ <GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath>
diff --git a/src/SMAPI.ModBuildConfig/package.nuspec b/src/SMAPI.ModBuildConfig/package.nuspec
index 2512c4d6..66d02537 100644
--- a/src/SMAPI.ModBuildConfig/package.nuspec
+++ b/src/SMAPI.ModBuildConfig/package.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Pathoschild.Stardew.ModBuildConfig</id>
- <version>2.1-alpha20180414</version>
+ <version>2.1</version>
<title>Build package for SMAPI mods</title>
<authors>Pathoschild</authors>
<owners>Pathoschild</owners>
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 248809df..b7972fe1 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -65,6 +65,9 @@ namespace StardewModdingAPI.Framework
/// <param name="api">The mod-provided API.</param>
IModMetadata SetApi(object api);
+ /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
+ bool HasManifest();
+
/// <summary>Whether the mod has at least one update key set.</summary>
bool HasUpdateKeys();
}
diff --git a/src/SMAPI/Framework/Input/InputState.cs b/src/SMAPI/Framework/Input/InputState.cs
deleted file mode 100644
index 62337a6c..00000000
--- a/src/SMAPI/Framework/Input/InputState.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Input;
-using StardewValley;
-
-namespace StardewModdingAPI.Framework.Input
-{
- /// <summary>A summary of input changes during an update frame.</summary>
- internal class InputState
- {
- /*********
- ** Accessors
- *********/
- /// <summary>The maximum amount of direction to ignore for the left thumbstick.</summary>
- private const float LeftThumbstickDeadZone = 0.2f;
-
- /// <summary>The maximum amount of direction to ignore for the right thumbstick.</summary>
- private const float RightThumbstickDeadZone = 0f;
-
-
- /*********
- ** Accessors
- *********/
- /// <summary>The underlying controller state.</summary>
- public GamePadState ControllerState { get; }
-
- /// <summary>The underlying keyboard state.</summary>
- public KeyboardState KeyboardState { get; }
-
- /// <summary>The underlying mouse state.</summary>
- public MouseState MouseState { get; }
-
- /// <summary>The mouse position on the screen adjusted for the zoom level.</summary>
- public Point MousePosition { get; }
-
- /// <summary>The buttons which were pressed, held, or released.</summary>
- public IDictionary<SButton, InputStatus> ActiveButtons { get; } = new Dictionary<SButton, InputStatus>();
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an empty instance.</summary>
- public InputState() { }
-
- /// <summary>Construct an instance.</summary>
- /// <param name="previousState">The previous input state.</param>
- /// <param name="controllerState">The current controller state.</param>
- /// <param name="keyboardState">The current keyboard state.</param>
- /// <param name="mouseState">The current mouse state.</param>
- public InputState(InputState previousState, GamePadState controllerState, KeyboardState keyboardState, MouseState mouseState)
- {
- // init properties
- this.ControllerState = controllerState;
- this.KeyboardState = keyboardState;
- this.MouseState = mouseState;
- this.MousePosition = new Point((int)(mouseState.X * (1.0 / Game1.options.zoomLevel)), (int)(mouseState.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX
-
- // get button states
- SButton[] down = this.GetPressedButtons(keyboardState, mouseState, controllerState).ToArray();
- foreach (SButton button in down)
- this.ActiveButtons[button] = this.GetStatus(previousState.GetStatus(button), isDown: true);
- foreach (KeyValuePair<SButton, InputStatus> prev in previousState.ActiveButtons)
- {
- if (prev.Value.IsDown() && !this.ActiveButtons.ContainsKey(prev.Key))
- this.ActiveButtons[prev.Key] = InputStatus.Released;
- }
- }
-
- /// <summary>Get the status of a button.</summary>
- /// <param name="button">The button to check.</param>
- public InputStatus GetStatus(SButton button)
- {
- return this.ActiveButtons.TryGetValue(button, out InputStatus status) ? status : InputStatus.None;
- }
-
- /// <summary>Get whether a given button was pressed or held.</summary>
- /// <param name="button">The button to check.</param>
- public bool IsDown(SButton button)
- {
- return this.GetStatus(button).IsDown();
- }
-
- /// <summary>Get whether any of the given buttons were pressed or held.</summary>
- /// <param name="buttons">The buttons to check.</param>
- public bool IsAnyDown(InputButton[] buttons)
- {
- return buttons.Any(button => this.IsDown(button.ToSButton()));
- }
-
- /// <summary>Get the current input state.</summary>
- /// <param name="previousState">The previous input state.</param>
- public static InputState GetState(InputState previousState)
- {
- GamePadState controllerState = GamePad.GetState(PlayerIndex.One);
- KeyboardState keyboardState = Keyboard.GetState();
- MouseState mouseState = Mouse.GetState();
-
- return new InputState(previousState, controllerState, keyboardState, mouseState);
- }
-
- /*********
- ** Private methods
- *********/
- /// <summary>Get the status of a button.</summary>
- /// <param name="oldStatus">The previous button status.</param>
- /// <param name="isDown">Whether the button is currently down.</param>
- public InputStatus GetStatus(InputStatus oldStatus, bool isDown)
- {
- if (isDown && oldStatus.IsDown())
- return InputStatus.Held;
- if (isDown)
- return InputStatus.Pressed;
- return InputStatus.Released;
- }
-
- /// <summary>Get the buttons pressed in the given stats.</summary>
- /// <param name="keyboard">The keyboard state.</param>
- /// <param name="mouse">The mouse state.</param>
- /// <param name="controller">The controller state.</param>
- /// <remarks>Thumbstick direction logic derived from <see cref="ButtonCollection"/>.</remarks>
- private IEnumerable<SButton> GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller)
- {
- // keyboard
- foreach (Keys key in keyboard.GetPressedKeys())
- yield return key.ToSButton();
-
- // mouse
- if (mouse.LeftButton == ButtonState.Pressed)
- yield return SButton.MouseLeft;
- if (mouse.RightButton == ButtonState.Pressed)
- yield return SButton.MouseRight;
- if (mouse.MiddleButton == ButtonState.Pressed)
- yield return SButton.MouseMiddle;
- if (mouse.XButton1 == ButtonState.Pressed)
- yield return SButton.MouseX1;
- if (mouse.XButton2 == ButtonState.Pressed)
- yield return SButton.MouseX2;
-
- // controller
- if (controller.IsConnected)
- {
- // main buttons
- if (controller.Buttons.A == ButtonState.Pressed)
- yield return SButton.ControllerA;
- if (controller.Buttons.B == ButtonState.Pressed)
- yield return SButton.ControllerB;
- if (controller.Buttons.X == ButtonState.Pressed)
- yield return SButton.ControllerX;
- if (controller.Buttons.Y == ButtonState.Pressed)
- yield return SButton.ControllerY;
- if (controller.Buttons.LeftStick == ButtonState.Pressed)
- yield return SButton.LeftStick;
- if (controller.Buttons.RightStick == ButtonState.Pressed)
- yield return SButton.RightStick;
- if (controller.Buttons.Start == ButtonState.Pressed)
- yield return SButton.ControllerStart;
-
- // directional pad
- if (controller.DPad.Up == ButtonState.Pressed)
- yield return SButton.DPadUp;
- if (controller.DPad.Down == ButtonState.Pressed)
- yield return SButton.DPadDown;
- if (controller.DPad.Left == ButtonState.Pressed)
- yield return SButton.DPadLeft;
- if (controller.DPad.Right == ButtonState.Pressed)
- yield return SButton.DPadRight;
-
- // secondary buttons
- if (controller.Buttons.Back == ButtonState.Pressed)
- yield return SButton.ControllerBack;
- if (controller.Buttons.BigButton == ButtonState.Pressed)
- yield return SButton.BigButton;
-
- // shoulders
- if (controller.Buttons.LeftShoulder == ButtonState.Pressed)
- yield return SButton.LeftShoulder;
- if (controller.Buttons.RightShoulder == ButtonState.Pressed)
- yield return SButton.RightShoulder;
-
- // triggers
- if (controller.Triggers.Left > 0.2f)
- yield return SButton.LeftTrigger;
- if (controller.Triggers.Right > 0.2f)
- yield return SButton.RightTrigger;
-
- // left thumbstick direction
- if (controller.ThumbSticks.Left.Y > InputState.LeftThumbstickDeadZone)
- yield return SButton.LeftThumbstickUp;
- if (controller.ThumbSticks.Left.Y < -InputState.LeftThumbstickDeadZone)
- yield return SButton.LeftThumbstickDown;
- if (controller.ThumbSticks.Left.X > InputState.LeftThumbstickDeadZone)
- yield return SButton.LeftThumbstickRight;
- if (controller.ThumbSticks.Left.X < -InputState.LeftThumbstickDeadZone)
- yield return SButton.LeftThumbstickLeft;
-
- // right thumbstick direction
- if (this.IsRightThumbstickOutsideDeadZone(controller.ThumbSticks.Right))
- {
- if (controller.ThumbSticks.Right.Y > 0)
- yield return SButton.RightThumbstickUp;
- if (controller.ThumbSticks.Right.Y < 0)
- yield return SButton.RightThumbstickDown;
- if (controller.ThumbSticks.Right.X > 0)
- yield return SButton.RightThumbstickRight;
- if (controller.ThumbSticks.Right.X < 0)
- yield return SButton.RightThumbstickLeft;
- }
- }
- }
-
- /// <summary>Get whether the right thumbstick should be considered outside the dead zone.</summary>
- /// <param name="direction">The right thumbstick value.</param>
- private bool IsRightThumbstickOutsideDeadZone(Vector2 direction)
- {
- return direction.Length() > 0.9f;
- }
- }
-}
diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs
new file mode 100644
index 00000000..62defa9f
--- /dev/null
+++ b/src/SMAPI/Framework/Input/SInputState.cs
@@ -0,0 +1,359 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using StardewValley;
+
+#pragma warning disable 809 // obsolete override of non-obsolete method (this is deliberate)
+namespace StardewModdingAPI.Framework.Input
+{
+ /// <summary>A summary of input changes during an update frame.</summary>
+ internal sealed class SInputState : InputState
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The maximum amount of direction to ignore for the left thumbstick.</summary>
+ private const float LeftThumbstickDeadZone = 0.2f;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The controller state as of the last update.</summary>
+ public GamePadState RealController { get; private set; }
+
+ /// <summary>The keyboard state as of the last update.</summary>
+ public KeyboardState RealKeyboard { get; private set; }
+
+ /// <summary>The mouse state as of the last update.</summary>
+ public MouseState RealMouse { get; private set; }
+
+ /// <summary>A derivative of <see cref="RealController"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary>
+ public GamePadState SuppressedController { get; private set; }
+
+ /// <summary>A derivative of <see cref="RealKeyboard"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary>
+ public KeyboardState SuppressedKeyboard { get; private set; }
+
+ /// <summary>A derivative of <see cref="RealMouse"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary>
+ public MouseState SuppressedMouse { get; private set; }
+
+ /// <summary>The mouse position on the screen adjusted for the zoom level.</summary>
+ public Point MousePosition { get; private set; }
+
+ /// <summary>The buttons which were pressed, held, or released.</summary>
+ public IDictionary<SButton, InputStatus> ActiveButtons { get; private set; } = new Dictionary<SButton, InputStatus>();
+
+ /// <summary>The buttons to suppress when the game next handles input. Each button is suppressed until it's released.</summary>
+ public HashSet<SButton> SuppressButtons { get; } = new HashSet<SButton>();
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get a copy of the current state.</summary>
+ public SInputState Clone()
+ {
+ return new SInputState
+ {
+ ActiveButtons = this.ActiveButtons,
+ RealController = this.RealController,
+ RealKeyboard = this.RealKeyboard,
+ RealMouse = this.RealMouse,
+ MousePosition = this.MousePosition
+ };
+ }
+
+ /// <summary>This method is called by the game, and does nothing since SMAPI will already have updated by that point.</summary>
+ [Obsolete("This method should only be called by the game itself.")]
+ public override void Update() { }
+
+ /// <summary>Update the current button statuses for the given tick.</summary>
+ public void TrueUpdate()
+ {
+ try
+ {
+ // get new states
+ GamePadState realController = GamePad.GetState(PlayerIndex.One);
+ KeyboardState realKeyboard = Keyboard.GetState();
+ MouseState realMouse = Mouse.GetState();
+ Point mousePosition = new Point((int)(this.RealMouse.X * (1.0 / Game1.options.zoomLevel)), (int)(this.RealMouse.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX
+ var activeButtons = this.DeriveStatuses(this.ActiveButtons, realKeyboard, realMouse, realController);
+
+ // get suppressed states
+ GamePadState suppressedController = realController;
+ KeyboardState suppressedKeyboard = realKeyboard;
+ MouseState suppressedMouse = realMouse;
+ if (this.SuppressButtons.Count > 0)
+ this.UpdateSuppression(activeButtons, ref suppressedKeyboard, ref suppressedMouse, ref suppressedController);
+
+ // update
+ this.ActiveButtons = activeButtons;
+ this.RealController = realController;
+ this.RealKeyboard = realKeyboard;
+ this.RealMouse = realMouse;
+ this.SuppressedController = suppressedController;
+ this.SuppressedKeyboard = suppressedKeyboard;
+ this.SuppressedMouse = suppressedMouse;
+ this.MousePosition = mousePosition;
+ }
+ catch (InvalidOperationException)
+ {
+ // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true
+ }
+ }
+
+ /// <summary>Get the gamepad state visible to the game.</summary>
+ [Obsolete("This method should only be called by the game itself.")]
+ public override GamePadState GetGamePadState()
+ {
+ return this.ShouldSuppressNow()
+ ? this.SuppressedController
+ : this.RealController;
+ }
+
+ /// <summary>Get the keyboard state visible to the game.</summary>
+ [Obsolete("This method should only be called by the game itself.")]
+ public override KeyboardState GetKeyboardState()
+ {
+ return this.ShouldSuppressNow()
+ ? this.SuppressedKeyboard
+ : this.RealKeyboard;
+ }
+
+ /// <summary>Get the keyboard state visible to the game.</summary>
+ [Obsolete("This method should only be called by the game itself.")]
+ public override MouseState GetMouseState()
+ {
+ return this.ShouldSuppressNow()
+ ? this.SuppressedMouse
+ : this.RealMouse;
+ }
+
+ /// <summary>Get whether a given button was pressed or held.</summary>
+ /// <param name="button">The button to check.</param>
+ public bool IsDown(SButton button)
+ {
+ return this.GetStatus(this.ActiveButtons, button).IsDown();
+ }
+
+ /// <summary>Get whether any of the given buttons were pressed or held.</summary>
+ /// <param name="buttons">The buttons to check.</param>
+ public bool IsAnyDown(InputButton[] buttons)
+ {
+ return buttons.Any(button => this.IsDown(button.ToSButton()));
+ }
+
+ /// <summary>Apply input suppression for the given input states.</summary>
+ /// <param name="activeButtons">The current button states to check.</param>
+ /// <param name="keyboardState">The game's keyboard state for the current tick.</param>
+ /// <param name="mouseState">The game's mouse state for the current tick.</param>
+ /// <param name="gamePadState">The game's controller state for the current tick.</param>
+ public void UpdateSuppression(IDictionary<SButton, InputStatus> activeButtons, ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState)
+ {
+ // stop suppressing buttons once released
+ if (this.SuppressButtons.Count != 0)
+ this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown());
+ if (this.SuppressButtons.Count == 0)
+ return;
+
+ // gather info
+ HashSet<Keys> keyboardButtons = new HashSet<Keys>();
+ HashSet<SButton> controllerButtons = new HashSet<SButton>();
+ HashSet<SButton> mouseButtons = new HashSet<SButton>();
+ foreach (SButton button in this.SuppressButtons)
+ {
+ if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2)
+ mouseButtons.Add(button);
+ else if (button.TryGetKeyboard(out Keys key))
+ keyboardButtons.Add(key);
+ else if (gamePadState.IsConnected && button.TryGetController(out Buttons _))
+ controllerButtons.Add(button);
+ }
+
+ // suppress keyboard keys
+ if (keyboardState.GetPressedKeys().Any() && keyboardButtons.Any())
+ keyboardState = new KeyboardState(keyboardState.GetPressedKeys().Except(keyboardButtons).ToArray());
+
+ // suppress controller keys
+ if (gamePadState.IsConnected && controllerButtons.Any())
+ {
+ GamePadStateBuilder builder = new GamePadStateBuilder(gamePadState);
+ builder.SuppressButtons(controllerButtons);
+ gamePadState = builder.ToGamePadState();
+ }
+
+ // suppress mouse buttons
+ if (mouseButtons.Any())
+ {
+ mouseState = new MouseState(
+ x: mouseState.X,
+ y: mouseState.Y,
+ scrollWheel: mouseState.ScrollWheelValue,
+ leftButton: mouseButtons.Contains(SButton.MouseLeft) ? ButtonState.Pressed : mouseState.LeftButton,
+ middleButton: mouseButtons.Contains(SButton.MouseMiddle) ? ButtonState.Pressed : mouseState.MiddleButton,
+ rightButton: mouseButtons.Contains(SButton.MouseRight) ? ButtonState.Pressed : mouseState.RightButton,
+ xButton1: mouseButtons.Contains(SButton.MouseX1) ? ButtonState.Pressed : mouseState.XButton1,
+ xButton2: mouseButtons.Contains(SButton.MouseX2) ? ButtonState.Pressed : mouseState.XButton2
+ );
+ }
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Whether input should be suppressed in the current context.</summary>
+ private bool ShouldSuppressNow()
+ {
+ return Game1.chatBox != null && !Game1.chatBox.isActive();
+ }
+
+ /// <summary>Get the status of all pressed or released buttons relative to their previous status.</summary>
+ /// <param name="previousStatuses">The previous button statuses.</param>
+ /// <param name="keyboard">The keyboard state.</param>
+ /// <param name="mouse">The mouse state.</param>
+ /// <param name="controller">The controller state.</param>
+ private IDictionary<SButton, InputStatus> DeriveStatuses(IDictionary<SButton, InputStatus> previousStatuses, KeyboardState keyboard, MouseState mouse, GamePadState controller)
+ {
+ IDictionary<SButton, InputStatus> activeButtons = new Dictionary<SButton, InputStatus>();
+
+ // handle pressed keys
+ SButton[] down = this.GetPressedButtons(keyboard, mouse, controller).ToArray();
+ foreach (SButton button in down)
+ activeButtons[button] = this.DeriveStatus(this.GetStatus(previousStatuses, button), isDown: true);
+
+ // handle released keys
+ foreach (KeyValuePair<SButton, InputStatus> prev in activeButtons)
+ {
+ if (prev.Value.IsDown() && !activeButtons.ContainsKey(prev.Key))
+ activeButtons[prev.Key] = InputStatus.Released;
+ }
+
+ return activeButtons;
+ }
+
+ /// <summary>Get the status of a button relative to its previous status.</summary>
+ /// <param name="oldStatus">The previous button status.</param>
+ /// <param name="isDown">Whether the button is currently down.</param>
+ private InputStatus DeriveStatus(InputStatus oldStatus, bool isDown)
+ {
+ if (isDown && oldStatus.IsDown())
+ return InputStatus.Held;
+ if (isDown)
+ return InputStatus.Pressed;
+ return InputStatus.Released;
+ }
+
+ /// <summary>Get the status of a button.</summary>
+ /// <param name="activeButtons">The current button states to check.</param>
+ /// <param name="button">The button to check.</param>
+ private InputStatus GetStatus(IDictionary<SButton, InputStatus> activeButtons, SButton button)
+ {
+ return activeButtons.TryGetValue(button, out InputStatus status) ? status : InputStatus.None;
+ }
+
+ /// <summary>Get the buttons pressed in the given stats.</summary>
+ /// <param name="keyboard">The keyboard state.</param>
+ /// <param name="mouse">The mouse state.</param>
+ /// <param name="controller">The controller state.</param>
+ /// <remarks>Thumbstick direction logic derived from <see cref="ButtonCollection"/>.</remarks>
+ private IEnumerable<SButton> GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller)
+ {
+ // keyboard
+ foreach (Keys key in keyboard.GetPressedKeys())
+ yield return key.ToSButton();
+
+ // mouse
+ if (mouse.LeftButton == ButtonState.Pressed)
+ yield return SButton.MouseLeft;
+ if (mouse.RightButton == ButtonState.Pressed)
+ yield return SButton.MouseRight;
+ if (mouse.MiddleButton == ButtonState.Pressed)
+ yield return SButton.MouseMiddle;
+ if (mouse.XButton1 == ButtonState.Pressed)
+ yield return SButton.MouseX1;
+ if (mouse.XButton2 == ButtonState.Pressed)
+ yield return SButton.MouseX2;
+
+ // controller
+ if (controller.IsConnected)
+ {
+ // main buttons
+ if (controller.Buttons.A == ButtonState.Pressed)
+ yield return SButton.ControllerA;
+ if (controller.Buttons.B == ButtonState.Pressed)
+ yield return SButton.ControllerB;
+ if (controller.Buttons.X == ButtonState.Pressed)
+ yield return SButton.ControllerX;
+ if (controller.Buttons.Y == ButtonState.Pressed)
+ yield return SButton.ControllerY;
+ if (controller.Buttons.LeftStick == ButtonState.Pressed)
+ yield return SButton.LeftStick;
+ if (controller.Buttons.RightStick == ButtonState.Pressed)
+ yield return SButton.RightStick;
+ if (controller.Buttons.Start == ButtonState.Pressed)
+ yield return SButton.ControllerStart;
+
+ // directional pad
+ if (controller.DPad.Up == ButtonState.Pressed)
+ yield return SButton.DPadUp;
+ if (controller.DPad.Down == ButtonState.Pressed)
+ yield return SButton.DPadDown;
+ if (controller.DPad.Left == ButtonState.Pressed)
+ yield return SButton.DPadLeft;
+ if (controller.DPad.Right == ButtonState.Pressed)
+ yield return SButton.DPadRight;
+
+ // secondary buttons
+ if (controller.Buttons.Back == ButtonState.Pressed)
+ yield return SButton.ControllerBack;
+ if (controller.Buttons.BigButton == ButtonState.Pressed)
+ yield return SButton.BigButton;
+
+ // shoulders
+ if (controller.Buttons.LeftShoulder == ButtonState.Pressed)
+ yield return SButton.LeftShoulder;
+ if (controller.Buttons.RightShoulder == ButtonState.Pressed)
+ yield return SButton.RightShoulder;
+
+ // triggers
+ if (controller.Triggers.Left > 0.2f)
+ yield return SButton.LeftTrigger;
+ if (controller.Triggers.Right > 0.2f)
+ yield return SButton.RightTrigger;
+
+ // left thumbstick direction
+ if (controller.ThumbSticks.Left.Y > SInputState.LeftThumbstickDeadZone)
+ yield return SButton.LeftThumbstickUp;
+ if (controller.ThumbSticks.Left.Y < -SInputState.LeftThumbstickDeadZone)
+ yield return SButton.LeftThumbstickDown;
+ if (controller.ThumbSticks.Left.X > SInputState.LeftThumbstickDeadZone)
+ yield return SButton.LeftThumbstickRight;
+ if (controller.ThumbSticks.Left.X < -SInputState.LeftThumbstickDeadZone)
+ yield return SButton.LeftThumbstickLeft;
+
+ // right thumbstick direction
+ if (this.IsRightThumbstickOutsideDeadZone(controller.ThumbSticks.Right))
+ {
+ if (controller.ThumbSticks.Right.Y > 0)
+ yield return SButton.RightThumbstickUp;
+ if (controller.ThumbSticks.Right.Y < 0)
+ yield return SButton.RightThumbstickDown;
+ if (controller.ThumbSticks.Right.X > 0)
+ yield return SButton.RightThumbstickRight;
+ if (controller.ThumbSticks.Right.X < 0)
+ yield return SButton.RightThumbstickLeft;
+ }
+ }
+ }
+
+ /// <summary>Get whether the right thumbstick should be considered outside the dead zone.</summary>
+ /// <param name="direction">The right thumbstick value.</param>
+ private bool IsRightThumbstickOutsideDeadZone(Vector2 direction)
+ {
+ return direction.Length() > 0.9f;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index af888b71..d3a33e7a 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -104,11 +104,18 @@ namespace StardewModdingAPI.Framework.ModLoading
return this;
}
+ /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
+ public bool HasManifest()
+ {
+ return this.Manifest != null;
+ }
+
/// <summary>Whether the mod has at least one update key set.</summary>
public bool HasUpdateKeys()
{
return
- this.Manifest?.UpdateKeys != null
+ this.HasManifest()
+ && this.Manifest.UpdateKeys != null
&& this.Manifest.UpdateKeys.Any(key => !string.IsNullOrWhiteSpace(key));
}
}
diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs
index a76afc3c..cc511ed4 100644
--- a/src/SMAPI/Framework/Monitor.cs
+++ b/src/SMAPI/Framework/Monitor.cs
@@ -48,9 +48,6 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether to write anything to the console. This should be disabled if no console is available.</summary>
internal bool WriteToConsole { get; set; } = true;
- /// <summary>Whether to write anything to the log file. This should almost always be enabled.</summary>
- internal bool WriteToFile { get; set; } = true;
-
/*********
** Public methods
@@ -96,8 +93,16 @@ namespace StardewModdingAPI.Framework
{
if (this.WriteToConsole)
this.ConsoleManager.ExclusiveWriteWithoutInterception(Console.WriteLine);
- if (this.WriteToFile)
- this.LogFile.WriteLine("");
+ this.LogFile.WriteLine("");
+ }
+
+ /// <summary>Log console input from the user.</summary>
+ /// <param name="input">The user input to log.</param>
+ internal void LogUserInput(string input)
+ {
+ // user input already appears in the console, so just need to write to file
+ string prefix = this.GenerateMessagePrefix(this.Source, LogLevel.Info);
+ this.LogFile.WriteLine($"{prefix} $>{input}");
}
@@ -120,9 +125,8 @@ namespace StardewModdingAPI.Framework
private void LogImpl(string source, string message, LogLevel level, ConsoleColor color, ConsoleColor? background = null)
{
// generate message
- string levelStr = level.ToString().ToUpper().PadRight(Monitor.MaxLevelLength);
-
- string fullMessage = $"[{DateTime.Now:HH:mm:ss} {levelStr} {source}] {message}";
+ string prefix = this.GenerateMessagePrefix(source, level);
+ string fullMessage = $"{prefix} {message}";
string consoleMessage = this.ShowFullStampInConsole ? fullMessage : $"[{source}] {message}";
// write to console
@@ -144,8 +148,16 @@ namespace StardewModdingAPI.Framework
}
// write to log file
- if (this.WriteToFile)
- this.LogFile.WriteLine(fullMessage);
+ this.LogFile.WriteLine(fullMessage);
+ }
+
+ /// <summary>Generate a message prefix for the current time.</summary>
+ /// <param name="source">The name of the mod logging the message.</param>
+ /// <param name="level">The log level.</param>
+ private string GenerateMessagePrefix(string source, LogLevel level)
+ {
+ string levelStr = level.ToString().ToUpper().PadRight(Monitor.MaxLevelLength);
+ return $"[{DateTime.Now:HH:mm:ss} {levelStr} {source}]";
}
/// <summary>Get the color scheme to use for the current console.</summary>
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 4f5bd96b..182b90fc 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -53,6 +53,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Manages SMAPI events for mods.</summary>
private readonly EventManager Events;
+ /// <summary>Manages input visible to the game.</summary>
+ private SInputState Input => (SInputState)Game1.input;
+
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from a draw error.</summary>
private readonly Countdown DrawCrashTimer = new Countdown(60); // 60 ticks = roughly one second
@@ -78,9 +81,6 @@ namespace StardewModdingAPI.Framework
/****
** Game state
****/
- /// <summary>The player input as of the previous tick.</summary>
- private InputState PreviousInput = new InputState();
-
/// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary>
private readonly List<IWatcher> Watchers = new List<IWatcher>();
@@ -120,9 +120,6 @@ namespace StardewModdingAPI.Framework
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection;
- /// <summary>The buttons to suppress when the game next handles input. Each button is suppressed until it's released.</summary>
- private readonly HashSet<SButton> SuppressButtons = new HashSet<SButton>();
-
/*********
** Accessors
@@ -157,7 +154,7 @@ namespace StardewModdingAPI.Framework
this.OnGameExiting = onGameExiting;
if (this.ContentCore == null) // shouldn't happen since CreateContentManager is called first, but let's init here just in case
this.ContentCore = new ContentCore(this.Content.ServiceProvider, this.Content.RootDirectory, Thread.CurrentThread.CurrentUICulture, this.Monitor, reflection);
- Game1.hooks = new SModHooks(this.UpdateControlInput);
+ Game1.input = new SInputState();
// init watchers
Game1.locations = new ObservableCollection<GameLocation>();
@@ -289,7 +286,7 @@ namespace StardewModdingAPI.Framework
if (this.FirstUpdate)
this.OnGameInitialised();
-
+
/*********
** Update context
*********/
@@ -390,16 +387,9 @@ namespace StardewModdingAPI.Framework
*********/
if (Game1.game1.IsActive)
{
- // get input state
- InputState inputState;
- try
- {
- inputState = InputState.GetState(this.PreviousInput);
- }
- catch (InvalidOperationException) // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true
- {
- inputState = this.PreviousInput;
- }
+ SInputState previousInputState = this.Input.Clone();
+ SInputState inputState = this.Input;
+ inputState.TrueUpdate();
// raise events
bool isChatInput = Game1.IsChatting || (Context.IsMultiplayer && Context.IsWorldReady && Game1.activeClickableMenu == null && Game1.currentMinigame == null && inputState.IsAnyDown(Game1.options.chatButton));
@@ -425,7 +415,7 @@ namespace StardewModdingAPI.Framework
if (status == InputStatus.Pressed)
{
- this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons));
+ this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), inputState.SuppressButtons));
// legacy events
if (button.TryGetKeyboard(out Keys key))
@@ -436,14 +426,14 @@ namespace StardewModdingAPI.Framework
else if (button.TryGetController(out Buttons controllerButton))
{
if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger)
- this.Events.Control_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.ControllerState.Triggers.Left : inputState.ControllerState.Triggers.Right));
+ this.Events.Control_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
else
this.Events.Control_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton));
}
}
else if (status == InputStatus.Released)
{
- this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons));
+ this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), inputState.SuppressButtons));
// legacy events
if (button.TryGetKeyboard(out Keys key))
@@ -454,7 +444,7 @@ namespace StardewModdingAPI.Framework
else if (button.TryGetController(out Buttons controllerButton))
{
if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger)
- this.Events.Control_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.ControllerState.Triggers.Left : inputState.ControllerState.Triggers.Right));
+ this.Events.Control_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
else
this.Events.Control_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton));
}
@@ -462,14 +452,11 @@ namespace StardewModdingAPI.Framework
}
// raise legacy state-changed events
- if (inputState.KeyboardState != this.PreviousInput.KeyboardState)
- this.Events.Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(this.PreviousInput.KeyboardState, inputState.KeyboardState));
- if (inputState.MouseState != this.PreviousInput.MouseState)
- this.Events.Control_MouseChanged.Raise(new EventArgsMouseStateChanged(this.PreviousInput.MouseState, inputState.MouseState, this.PreviousInput.MousePosition, inputState.MousePosition));
+ if (inputState.RealKeyboard != previousInputState.RealKeyboard)
+ this.Events.Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard));
+ if (inputState.RealMouse != previousInputState.RealMouse)
+ this.Events.Control_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, previousInputState.MousePosition, inputState.MousePosition));
}
-
- // track state
- this.PreviousInput = inputState;
}
/*********
@@ -624,17 +611,6 @@ namespace StardewModdingAPI.Framework
}
}
- /// <summary>Read the current input state for handling.</summary>
- /// <param name="keyboardState">The game's keyboard state for the current tick.</param>
- /// <param name="mouseState">The game's mouse state for the current tick.</param>
- /// <param name="gamePadState">The game's controller state for the current tick.</param>
- /// <param name="defaultLogic">The game's default logic.</param>
- public void UpdateControlInput(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action defaultLogic)
- {
- this.ApplySuppression(ref keyboardState, ref mouseState, ref gamePadState);
- defaultLogic();
- }
-
/// <summary>The method called to draw everything to the screen.</summary>
/// <param name="gameTime">A snapshot of the game timing state.</param>
protected override void Draw(GameTime gameTime)
@@ -1256,63 +1232,6 @@ namespace StardewModdingAPI.Framework
/****
** Methods
****/
- /// <summary>Apply input suppression for the given input states.</summary>
- /// <param name="keyboardState">The game's keyboard state for the current tick.</param>
- /// <param name="mouseState">The game's mouse state for the current tick.</param>
- /// <param name="gamePadState">The game's controller state for the current tick.</param>
- private void ApplySuppression(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState)
- {
- // stop suppressing buttons once released
- if (this.SuppressButtons.Count != 0)
- {
- InputState inputState = new InputState(this.PreviousInput, gamePadState, keyboardState, mouseState);
- this.SuppressButtons.RemoveWhere(p => !inputState.IsDown(p));
- }
- if (this.SuppressButtons.Count == 0)
- return;
-
- // gather info
- HashSet<Keys> keyboardButtons = new HashSet<Keys>();
- HashSet<SButton> controllerButtons = new HashSet<SButton>();
- HashSet<SButton> mouseButtons = new HashSet<SButton>();
- foreach (SButton button in this.SuppressButtons)
- {
- if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2)
- mouseButtons.Add(button);
- else if (button.TryGetKeyboard(out Keys key))
- keyboardButtons.Add(key);
- else if (gamePadState.IsConnected && button.TryGetController(out Buttons _))
- controllerButtons.Add(button);
- }
-
- // suppress keyboard keys
- if (keyboardState.GetPressedKeys().Any() && keyboardButtons.Any())
- keyboardState = new KeyboardState(keyboardState.GetPressedKeys().Except(keyboardButtons).ToArray());
-
- // suppress controller keys
- if (gamePadState.IsConnected && controllerButtons.Any())
- {
- GamePadStateBuilder builder = new GamePadStateBuilder(gamePadState);
- builder.SuppressButtons(controllerButtons);
- gamePadState = builder.ToGamePadState();
- }
-
- // suppress mouse buttons
- if (mouseButtons.Any())
- {
- mouseState = new MouseState(
- x: mouseState.X,
- y: mouseState.Y,
- scrollWheel: mouseState.ScrollWheelValue,
- leftButton: mouseButtons.Contains(SButton.MouseLeft) ? ButtonState.Pressed : mouseState.LeftButton,
- middleButton: mouseButtons.Contains(SButton.MouseMiddle) ? ButtonState.Pressed : mouseState.MiddleButton,
- rightButton: mouseButtons.Contains(SButton.MouseRight) ? ButtonState.Pressed : mouseState.RightButton,
- xButton1: mouseButtons.Contains(SButton.MouseX1) ? ButtonState.Pressed : mouseState.XButton1,
- xButton2: mouseButtons.Contains(SButton.MouseX2) ? ButtonState.Pressed : mouseState.XButton2
- );
- }
- }
-
/// <summary>Perform any cleanup needed when the player unloads a save and returns to the title screen.</summary>
private void CleanupAfterReturnToTitle()
{
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 3c6b1cf6..f1152d82 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -448,6 +448,9 @@ namespace StardewModdingAPI
if (string.IsNullOrWhiteSpace(input))
continue;
+ // write input to log file
+ this.Monitor.LogUserInput(input);
+
// parse input
try
{
@@ -582,25 +585,8 @@ namespace StardewModdingAPI
StringComparer.InvariantCultureIgnoreCase
);
- // report update keys
- {
- IModMetadata[] modsWithoutKeys = (
- from mod in mods
- where
- mod.Manifest != null
- && (mod.Manifest.UpdateKeys == null || !mod.Manifest.UpdateKeys.Any())
- && (mod.Manifest?.UniqueID != "SMAPI.ConsoleCommands" && mod.Manifest?.UniqueID != "SMAPI.TrainerMod")
- orderby mod.DisplayName
- select mod
- ).ToArray();
-
- string message = $"Checking {modsByKey.Count} mod update keys.";
- if (modsWithoutKeys.Any())
- message += $" {modsWithoutKeys.Length} mods have no update keys: {string.Join(", ", modsWithoutKeys.Select(p => p.DisplayName))}.";
- this.Monitor.Log($" {message}", LogLevel.Trace);
- }
-
// fetch results
+ this.Monitor.Log($" Checking {modsByKey.Count} mod update keys.", LogLevel.Trace);
var results =
(
from entry in client.GetModInfo(modsByKey.Keys.ToArray())
@@ -714,10 +700,12 @@ namespace StardewModdingAPI
// load content packs
foreach (IModMetadata metadata in mods.Where(p => p.IsContentPack))
{
- // get basic info
- IManifest manifest = metadata.Manifest;
this.Monitor.Log($" {metadata.DisplayName} (content pack, {PathUtilities.GetRelativePath(Constants.ModPath, metadata.DirectoryPath)})...", LogLevel.Trace);
+ // show warning for missing update key
+ if (metadata.HasManifest() && !metadata.HasUpdateKeys())
+ this.Monitor.Log($" {metadata.DisplayName} has no {nameof(IManifest.UpdateKeys)} in its manifest. You may not see update alerts for this mod.", LogLevel.Warn);
+
// validate status
if (metadata.Status == ModMetadataStatus.Failed)
{
@@ -726,11 +714,8 @@ namespace StardewModdingAPI
continue;
}
- // show warnings
- if (this.Settings.DeveloperMode && !metadata.HasUpdateKeys())
- this.Monitor.Log($" {metadata.DisplayName} has no {nameof(IManifest.UpdateKeys)} in its manifest. You may not see update alerts for this mod.", LogLevel.Warn);
-
// load mod as content pack
+ IManifest manifest = metadata.Manifest;
IMonitor monitor = this.GetSecondaryMonitor(metadata.DisplayName);
ContentManagerShim contentManager = this.ContentCore.CreateContentManager($"Mods.{metadata.Manifest.UniqueID}", metadata.DirectoryPath);
IContentHelper contentHelper = new ContentHelper(this.ContentCore, contentManager, metadata.DirectoryPath, manifest.UniqueID, metadata.DisplayName, monitor);
@@ -766,6 +751,10 @@ namespace StardewModdingAPI
? $" {metadata.DisplayName} ({PathUtilities.GetRelativePath(Constants.ModPath, metadata.DirectoryPath)}{Path.DirectorySeparatorChar}{metadata.Manifest.EntryDll})..." // don't use Path.Combine here, since EntryDLL might not be valid
: $" {metadata.DisplayName}...", LogLevel.Trace);
+ // show warnings
+ if (metadata.HasManifest() && !metadata.HasUpdateKeys() && metadata.Manifest.UniqueID != "SMAPI.ConsoleCommands")
+ this.Monitor.Log($" {metadata.DisplayName} has no {nameof(IManifest.UpdateKeys)} in its manifest. You may not see update alerts for this mod.", LogLevel.Warn);
+
// validate status
if (metadata.Status == ModMetadataStatus.Failed)
{
@@ -774,10 +763,6 @@ namespace StardewModdingAPI
continue;
}
- // show warnings
- if (this.Settings.DeveloperMode && !metadata.HasUpdateKeys() && metadata.Manifest.UniqueID != "SMAPI.ConsoleCommands")
- this.Monitor.Log($" {metadata.DisplayName} has no {nameof(IManifest.UpdateKeys)} in its manifest. You may not see update alerts for this mod.", LogLevel.Warn);
-
// load mod
string assemblyPath = metadata.Manifest?.EntryDll != null
? Path.Combine(metadata.DirectoryPath, metadata.Manifest.EntryDll)
diff --git a/src/SMAPI/SModHooks.cs b/src/SMAPI/SModHooks.cs
deleted file mode 100644
index 1b88d606..00000000
--- a/src/SMAPI/SModHooks.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using Microsoft.Xna.Framework.Input;
-using StardewValley;
-
-namespace StardewModdingAPI
-{
- /// <summary>Intercepts predefined Stardew Valley mod hooks.</summary>
- internal class SModHooks : ModHooks
- {
- /*********
- ** Delegates
- *********/
- /// <summary>A delegate invoked by the <see cref="SModHooks.OnGame1_UpdateControlInput"/> hook.</summary>
- /// <param name="keyboardState">The game's keyboard state for the current tick.</param>
- /// <param name="mouseState">The game's mouse state for the current tick.</param>
- /// <param name="gamePadState">The game's controller state for the current tick.</param>
- /// <param name="action">The game's default logic.</param>
- public delegate void UpdateControlInputDelegate(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action action);
-
-
- /*********
- ** Properties
- *********/
- /// <summary>The callback for <see cref="OnGame1_UpdateControlInput"/>.</summary>
- private readonly UpdateControlInputDelegate UpdateControlInputHandler;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="updateControlInputHandler">The callback for <see cref="OnGame1_UpdateControlInput"/>.</param>
- public SModHooks(UpdateControlInputDelegate updateControlInputHandler)
- {
- this.UpdateControlInputHandler = updateControlInputHandler;
- }
-
- /// <summary>A hook invoked before the game processes player input.</summary>
- /// <param name="keyboardState">The game's keyboard state for the current tick.</param>
- /// <param name="mouseState">The game's mouse state for the current tick.</param>
- /// <param name="gamePadState">The game's controller state for the current tick.</param>
- /// <param name="action">The game's default logic.</param>
- public override void OnGame1_UpdateControlInput(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action action)
- {
- this.UpdateControlInputHandler(ref keyboardState, ref mouseState, ref gamePadState, action);
- }
- }
-}
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index d7719e27..560d7bf4 100644
--- a/src/SMAPI/StardewModdingAPI.csproj
+++ b/src/SMAPI/StardewModdingAPI.csproj
@@ -92,7 +92,7 @@
<Compile Include="Framework\Content\ContentCache.cs" />
<Compile Include="Framework\Events\ManagedEventBase.cs" />
<Compile Include="Framework\Input\GamePadStateBuilder.cs" />
- <Compile Include="Framework\Input\InputState.cs" />
+ <Compile Include="Framework\Input\SInputState.cs" />
<Compile Include="Framework\Input\InputStatus.cs" />
<Compile Include="Framework\LegacyManifestVersion.cs" />
<Compile Include="Framework\ModData\ModDatabase.cs" />
@@ -263,7 +263,6 @@
<Compile Include="Framework\SGame.cs" />
<Compile Include="IReflectionHelper.cs" />
<Compile Include="SemanticVersion.cs" />
- <Compile Include="SModHooks.cs" />
<Compile Include="Translation.cs" />
<Compile Include="ICursorPosition.cs" />
<Compile Include="Utilities\SDate.cs" />