Fix Virtual Keyboard mod

This commit is contained in:
zhiyang7 2023-02-06 16:09:33 +08:00
parent 0f94f4609d
commit a406411a04
10 changed files with 269 additions and 289 deletions

View File

@ -1,74 +1,58 @@
using System; using System;
using System.Collections.Concurrent; using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Events; using StardewModdingAPI.Events;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Input;
using StardewValley; using StardewValley;
using StardewValley.Menus; using StardewValley.Menus;
using System.Reflection;
using Microsoft.Xna.Framework.Input;
using static StardewModdingAPI.Mods.VirtualKeyboard.ModConfig; using static StardewModdingAPI.Mods.VirtualKeyboard.ModConfig;
using System.Threading.Tasks;
using Microsoft.Xna.Framework.Graphics;
namespace StardewModdingAPI.Mods.VirtualKeyboard namespace StardewModdingAPI.Mods.VirtualKeyboard
{ {
class KeyButton class KeyButton
{ {
private readonly IModHelper helper; private readonly Rectangle ButtonRectangle;
private readonly IMonitor Monitor;
private readonly Rectangle buttonRectangle;
private object buttonPressed; private readonly SButton ButtonKey;
private object buttonReleased; private readonly float Transparency;
private readonly string Alias;
private readonly MethodBase RaiseButtonPressed; private readonly string Command;
private readonly MethodBase RaiseButtonReleased; public bool Hidden;
private bool RaisingPressed;
private readonly SButton buttonKey; private bool RaisingReleased;
private readonly float transparency;
private readonly string alias;
private readonly string command;
public bool hidden;
private bool raisingPressed = false;
private bool raisingReleased = false;
public KeyButton(IModHelper helper, VirtualButton buttonDefine, IMonitor monitor) public KeyButton(IModHelper helper, VirtualButton buttonDefine, IMonitor monitor)
{ {
this.Monitor = monitor; this.Hidden = true;
this.helper = helper; this.ButtonRectangle = new Rectangle(buttonDefine.rectangle.X, buttonDefine.rectangle.Y, buttonDefine.rectangle.Width, buttonDefine.rectangle.Height);
this.hidden = true; this.ButtonKey = buttonDefine.key;
this.buttonRectangle = new Rectangle(buttonDefine.rectangle.X, buttonDefine.rectangle.Y, buttonDefine.rectangle.Width, buttonDefine.rectangle.Height);
this.buttonKey = buttonDefine.key;
if (buttonDefine.alias == null) if (buttonDefine.alias == null)
this.alias = this.buttonKey.ToString(); this.Alias = this.ButtonKey.ToString();
else else
this.alias = buttonDefine.alias; this.Alias = buttonDefine.alias;
this.command = buttonDefine.command; this.Command = buttonDefine.command;
if (buttonDefine.transparency <= 0.01f || buttonDefine.transparency > 1f) if (buttonDefine.transparency <= 0.01f || buttonDefine.transparency > 1f)
{ {
buttonDefine.transparency = 0.5f; buttonDefine.transparency = 0.5f;
} }
this.transparency = buttonDefine.transparency; this.Transparency = buttonDefine.transparency;
helper.Events.Display.Rendered += this.OnRendered; helper.Events.Display.Rendered += this.OnRendered;
helper.Events.Input.ButtonReleased += this.EventInputButtonReleased; helper.Events.Input.ButtonReleased += this.EventInputButtonReleased;
helper.Events.Input.ButtonPressed += this.EventInputButtonPressed; helper.Events.Input.ButtonPressed += this.EventInputButtonPressed;
} }
private object GetSCore(IModHelper helper) private bool ShouldTrigger(Vector2 screenPixels, SButton button)
{ {
MainActivity activity = this.helper.Reflection.GetField<MainActivity>(typeof(MainActivity), "instance").GetValue(); if (this.ButtonRectangle.Contains(screenPixels.X * Game1.options.zoomLevel, screenPixels.Y * Game1.options.zoomLevel) && !this.Hidden && button == SButton.MouseLeft)
object score = activity.GetType().GetField("core", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(activity);
return score;
}
private bool shouldTrigger(Vector2 screenPixels, SButton button)
{ {
if (this.buttonRectangle.Contains(screenPixels.X * Game1.options.zoomLevel, screenPixels.Y * Game1.options.zoomLevel) && !this.hidden && button == SButton.MouseLeft) if (!this.Hidden)
{
if (!this.hidden)
Toolbar.toolbarPressed = true; Toolbar.toolbarPressed = true;
return true; return true;
} }
@ -77,34 +61,34 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
private void EventInputButtonPressed(object sender, ButtonPressedEventArgs e) private void EventInputButtonPressed(object sender, ButtonPressedEventArgs e)
{ {
if (this.raisingPressed) if (this.RaisingPressed)
{ {
return; return;
} }
Vector2 screenPixels = e.Cursor.ScreenPixels; Vector2 screenPixels = e.Cursor.ScreenPixels;
if (this.buttonKey != SButton.None && this.shouldTrigger(screenPixels, e.Button)) if (this.ButtonKey != SButton.None && this.ShouldTrigger(screenPixels, e.Button))
{ {
object input = this.helper.Reflection.GetField<object>(typeof(Game1), "input").GetValue(); this.RaisingPressed = true;
this.raisingPressed = true; SInputState input = Game1.input as SInputState;
input.GetType().GetMethod("OverrideButton").Invoke(input, new object[] { this.buttonKey, true }); input?.OverrideButton(this.ButtonKey, true);
this.raisingPressed = false; this.RaisingPressed = false;
} }
} }
private void EventInputButtonReleased(object sender, ButtonReleasedEventArgs e) private void EventInputButtonReleased(object sender, ButtonReleasedEventArgs e)
{ {
if (this.raisingReleased) if (this.RaisingReleased)
{ {
return; return;
} }
Vector2 screenPixels = e.Cursor.ScreenPixels; Vector2 screenPixels = e.Cursor.ScreenPixels;
if (this.shouldTrigger(screenPixels, e.Button)) if (this.ShouldTrigger(screenPixels, e.Button))
{ {
if (this.buttonKey == SButton.RightWindows) if (this.ButtonKey == SButton.RightWindows)
{ {
KeyboardInput.Show("Command", "", "", false).ContinueWith<string>(delegate (Task<string> s) { KeyboardInput.Show("Command", "").ContinueWith(delegate (Task<string> s) {
string command; string command;
command = s.Result; command = s.Result;
if (command.Length > 0) if (command.Length > 0)
@ -115,36 +99,31 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
}); });
return; return;
} }
if (this.buttonKey == SButton.RightControl) if (this.ButtonKey == SButton.RightControl)
{ {
SGameConsole.Instance.Show(); SGameConsole.Instance.Show();
return; return;
} }
if (!string.IsNullOrEmpty(this.command)) if (!string.IsNullOrEmpty(this.Command))
{ {
this.SendCommand(this.command); this.SendCommand(this.Command);
return; return;
} }
object input = this.helper.Reflection.GetField<object>(typeof(Game1), "input").GetValue(); this.RaisingReleased = true;
this.raisingReleased = true; SInputState input = Game1.input as SInputState;
input.GetType().GetMethod("OverrideButton").Invoke(input, new object[] { this.buttonKey, false }); input?.OverrideButton(this.ButtonKey, false);
this.raisingReleased = false; this.RaisingReleased = false;
} }
} }
private void SendCommand(string command) private void SendCommand(string command)
{ {
object score = this.GetSCore(this.helper); SCore score = SMainActivity.Instance.core;
ConcurrentQueue<string> commandQueue = score.GetType().GetProperty("CommandQueue", BindingFlags.Public | BindingFlags.Instance)?.GetValue(score) as ConcurrentQueue<string>; CommandQueue commandQueue = score.RawCommandQueue;
if (commandQueue != null) if (commandQueue != null)
{ {
commandQueue.Enqueue(command); commandQueue.Add(command);
return;
} }
object sgame = score.GetType().GetField("Game", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(score);
commandQueue = sgame.GetType().GetProperty("CommandQueue", BindingFlags.Public | BindingFlags.Instance)?.GetValue(sgame) as ConcurrentQueue<string>;
commandQueue?.Enqueue(command);
} }
/// <summary>Raised before drawing the HUD (item toolbar, clock, etc) to the screen.</summary> /// <summary>Raised before drawing the HUD (item toolbar, clock, etc) to the screen.</summary>
@ -152,19 +131,20 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
/// <param name="e">The event arguments.</param> /// <param name="e">The event arguments.</param>
private void OnRendered(object sender, EventArgs e) private void OnRendered(object sender, EventArgs e)
{ {
if (!this.hidden) if (!this.Hidden)
{ {
float scale = this.transparency; float scale = this.Transparency;
if (!Game1.eventUp && Game1.activeClickableMenu is GameMenu == false && Game1.activeClickableMenu is ShopMenu == false && Game1.activeClickableMenu is IClickableMenu == false) if (!Game1.eventUp && Game1.activeClickableMenu is GameMenu == false && Game1.activeClickableMenu is ShopMenu == false && Game1.activeClickableMenu is IClickableMenu == false)
{ {
scale *= 0.5f; scale *= 0.5f;
} }
System.Reflection.FieldInfo matrixField = Game1.spriteBatch.GetType().GetField("_matrix", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); System.Reflection.FieldInfo spriteEffectField = Game1.spriteBatch.GetType().GetField("_spriteEffect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
object originMatrix = matrixField.GetValue(Game1.spriteBatch); SpriteEffect originSpriteEffect = spriteEffectField?.GetValue(Game1.spriteBatch) as SpriteEffect;
var originMatrix = originSpriteEffect?.TransformMatrix;
Game1.spriteBatch.End(); Game1.spriteBatch.End();
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, Microsoft.Xna.Framework.Matrix.CreateScale(1f)); Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(1f));
IClickableMenu.drawTextureBoxWithIconAndText(Game1.spriteBatch, Game1.smallFont, Game1.mouseCursors, new Rectangle(0x100, 0x100, 10, 10), null, new Rectangle(0, 0, 1, 1), IClickableMenu.drawTextureBoxWithIconAndText(Game1.spriteBatch, Game1.smallFont, Game1.mouseCursors, new Rectangle(0x100, 0x100, 10, 10), null, new Rectangle(0, 0, 1, 1),
this.alias, this.buttonRectangle.X, this.buttonRectangle.Y, this.buttonRectangle.Width, this.buttonRectangle.Height, Color.BurlyWood * scale, 4f, this.Alias, this.ButtonRectangle.X, this.ButtonRectangle.Y, this.ButtonRectangle.Width, this.ButtonRectangle.Height, Color.BurlyWood * scale, 4f,
true, false, true, false, false, false, false); // Remove bold to fix the text position issue true, false, true, false, false, false, false); // Remove bold to fix the text position issue
Game1.spriteBatch.End(); Game1.spriteBatch.End();
if(originMatrix != null) if(originMatrix != null)

View File

@ -2,18 +2,18 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
{ {
class ModConfig class ModConfig
{ {
public Toggle vToggle { get; set; } = new Toggle(new Rect(36, 12, 64, 64), true, SButton.None); public Toggle vToggle { get; set; } = new(new Rect(36, 12, 64, 64), true, SButton.None);
public VirtualButton[] buttons { get; set;} = new VirtualButton[] { public VirtualButton[] buttons { get; set;} = new VirtualButton[] {
new VirtualButton(SButton.Q, new Rect(190, 80, 90, 90), 0.5f), new(SButton.Q, new Rect(190, 80, 90, 90), 0.5f),
new VirtualButton(SButton.I, new Rect(290, 80, 90, 90), 0.5f), new(SButton.I, new Rect(290, 80, 90, 90), 0.5f),
new VirtualButton(SButton.O, new Rect(390, 80, 90, 90), 0.5f), new(SButton.O, new Rect(390, 80, 90, 90), 0.5f),
new VirtualButton(SButton.P, new Rect(490, 80, 90, 90), 0.5f) new(SButton.P, new Rect(490, 80, 90, 90), 0.5f)
}; };
public VirtualButton[] buttonsExtend { get; set; } = new VirtualButton[] { public VirtualButton[] buttonsExtend { get; set; } = new VirtualButton[] {
new VirtualButton(SButton.MouseRight, new Rect(190, 170, 162, 90), 0.5f, "RightMouse"), new(SButton.MouseRight, new Rect(190, 170, 162, 90), 0.5f, "RightMouse"),
new VirtualButton(SButton.None, new Rect(360, 170, 92, 90), 0.5f, "Zoom", "zoom 1.0"), new(SButton.None, new Rect(360, 170, 92, 90), 0.5f, "Zoom", "zoom 1.0"),
new VirtualButton(SButton.RightWindows, new Rect(460, 170, 162, 90), 0.5f, "Command"), new(SButton.RightWindows, new Rect(460, 170, 162, 90), 0.5f, "Command"),
new VirtualButton(SButton.RightControl, new Rect(630, 170, 162, 90), 0.5f, "Console") new(SButton.RightControl, new Rect(630, 170, 162, 90), 0.5f, "Console")
}; };
internal class VirtualButton { internal class VirtualButton {
public SButton key { get;set; } public SButton key { get;set; }

View File

@ -1,36 +1 @@
using System.Reflection; 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("StardewModdingAPI.Mods.VirtualKeyboard")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("StardewModdingAPI.Mods.VirtualKeyboard")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("29cce9c9-6811-415d-a681-a6d47073924d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,18 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{29CCE9C9-6811-415D-A681-A6D47073924D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StardewModdingAPI.Mods.VirtualKeyboard</RootNamespace> <RootNamespace>StardewModdingAPI.Mods.VirtualKeyboard</RootNamespace>
<AssemblyName>VirtualKeyboard</AssemblyName> <AssemblyName>VirtualKeyboard</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <TargetFramework>net5.0</TargetFramework>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -32,54 +28,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Mono.Android"> <Reference Include="MonoGame.Framework">
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\Mono.Android.dll</HintPath>
</Reference>
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\MonoGame.Framework.dll</HintPath> <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.5.6.31\assemblies\MonoGame.Framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="StardewModdingAPI"> <Reference Include="StardewModdingAPI">
<HintPath>..\SMAPI\bin\Release\StardewModdingAPI.dll</HintPath> <HintPath>..\SMAPI\bin\Debug\StardewModdingAPI.dll</HintPath>
</Reference> </Reference>
<Reference Include="StardewValley"> <Reference Include="StardewValley">
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\StardewValley.dll</HintPath> <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.5.6.31\assemblies\StardewValley.dll</HintPath>
</Reference> </Reference>
<Reference Include="System">
<Private>True</Private>
</Reference>
<Reference Include="System.Core">
<Private>True</Private>
</Reference>
<Reference Include="System.Drawing">
<Private>True</Private>
</Reference>
<Reference Include="System.Windows.Forms">
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<Private>True</Private>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data">
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http">
<Private>True</Private>
</Reference>
<Reference Include="System.Xml">
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="KeyButton.cs" />
<Compile Include="ModConfig.cs" />
<Compile Include="ModEntry.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VirtualToggle.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="manifest.json" /> <None Include="manifest.json" />
@ -93,5 +51,4 @@
<Name>SMAPI.Toolkit</Name> <Name>SMAPI.Toolkit</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -10,105 +10,105 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
{ {
class VirtualToggle class VirtualToggle
{ {
private readonly IModHelper helper; private readonly IModHelper Helper;
private readonly IMonitor Monitor; private readonly IMonitor Monitor;
private int enabledStage = 0; private int EnabledStage = 0;
private bool autoHidden = true; private bool AutoHidden = true;
private bool isDefault = true; private bool IsDefault = true;
private ClickableTextureComponent virtualToggleButton; private ClickableTextureComponent VirtualToggleButton;
private List<KeyButton> keyboard = new List<KeyButton>(); private List<KeyButton> Keyboard = new();
private List<KeyButton> keyboardExtend = new List<KeyButton>(); private List<KeyButton> KeyboardExtend = new();
private ModConfig modConfig; private ModConfig ModConfig;
private Texture2D texture; private Texture2D Texture;
private int lastPressTick = 0; private int LastPressTick = 0;
public VirtualToggle(IModHelper helper, IMonitor monitor) public VirtualToggle(IModHelper helper, IMonitor monitor)
{ {
this.Monitor = monitor; this.Monitor = monitor;
this.helper = helper; this.Helper = helper;
this.texture = this.helper.Content.Load<Texture2D>("assets/togglebutton.png", ContentSource.ModFolder); this.Texture = this.Helper.ModContent.Load<Texture2D>("assets/togglebutton.png");
this.modConfig = helper.ReadConfig<ModConfig>(); this.ModConfig = helper.ReadConfig<ModConfig>();
for (int i = 0; i < this.modConfig.buttons.Length; i++) for (int i = 0; i < this.ModConfig.buttons.Length; i++)
this.keyboard.Add(new KeyButton(helper, this.modConfig.buttons[i], this.Monitor)); this.Keyboard.Add(new KeyButton(helper, this.ModConfig.buttons[i], this.Monitor));
for (int i = 0; i < this.modConfig.buttonsExtend.Length; i++) for (int i = 0; i < this.ModConfig.buttonsExtend.Length; i++)
this.keyboardExtend.Add(new KeyButton(helper, this.modConfig.buttonsExtend[i], this.Monitor)); this.KeyboardExtend.Add(new KeyButton(helper, this.ModConfig.buttonsExtend[i], this.Monitor));
if (this.modConfig.vToggle.rectangle.X != 36 || this.modConfig.vToggle.rectangle.Y != 12) if (this.ModConfig.vToggle.rectangle.X != 36 || this.ModConfig.vToggle.rectangle.Y != 12)
this.isDefault = false; this.IsDefault = false;
this.autoHidden = this.modConfig.vToggle.autoHidden; this.AutoHidden = this.ModConfig.vToggle.autoHidden;
this.virtualToggleButton = new ClickableTextureComponent(new Rectangle(Game1.toolbarPaddingX + 64, 12, 128, 128), this.texture, new Rectangle(0, 0, 16, 16), 5.75f, false); this.VirtualToggleButton = new ClickableTextureComponent(new Rectangle(Game1.toolbarPaddingX + 64, 12, 128, 128), this.Texture, new Rectangle(0, 0, 16, 16), 5.75f, false);
helper.WriteConfig(this.modConfig); helper.WriteConfig(this.ModConfig);
this.helper.Events.Display.Rendered += this.OnRendered; this.Helper.Events.Display.Rendered += this.OnRendered;
this.helper.Events.Display.MenuChanged += this.OnMenuChanged; this.Helper.Events.Display.MenuChanged += this.OnMenuChanged;
this.helper.Events.Input.ButtonPressed += this.VirtualToggleButtonPressed; this.Helper.Events.Input.ButtonPressed += this.VirtualToggleButtonPressed;
} }
private void OnMenuChanged(object sender, MenuChangedEventArgs e) private void OnMenuChanged(object sender, MenuChangedEventArgs e)
{ {
if(this.autoHidden && e.NewMenu != null) { if(this.AutoHidden && e.NewMenu != null) {
foreach (var keys in this.keyboard) foreach (var keys in this.Keyboard)
{ {
keys.hidden = true; keys.Hidden = true;
} }
foreach (var keys in this.keyboardExtend) foreach (var keys in this.KeyboardExtend)
{ {
keys.hidden = true; keys.Hidden = true;
} }
this.enabledStage = 0; this.EnabledStage = 0;
} }
} }
private void VirtualToggleButtonPressed(object sender, ButtonPressedEventArgs e) private void VirtualToggleButtonPressed(object sender, ButtonPressedEventArgs e)
{ {
Vector2 screenPixels = e.Cursor.ScreenPixels; Vector2 screenPixels = e.Cursor.ScreenPixels;
if (this.modConfig.vToggle.key != SButton.None && e.Button == this.modConfig.vToggle.key) if (this.ModConfig.vToggle.key != SButton.None && e.Button == this.ModConfig.vToggle.key)
this.toggleLogic(); this.ToggleLogic();
else if (e.Button == SButton.MouseLeft && this.shouldTrigger(screenPixels)) else if (e.Button == SButton.MouseLeft && this.ShouldTrigger(screenPixels))
this.toggleLogic(); this.ToggleLogic();
} }
private void toggleLogic() private void ToggleLogic()
{ {
switch (this.enabledStage) switch (this.EnabledStage)
{ {
case 0: case 0:
foreach (var keys in this.keyboard) foreach (var keys in this.Keyboard)
{ {
keys.hidden = false; keys.Hidden = false;
} }
foreach (var keys in this.keyboardExtend) foreach (var keys in this.KeyboardExtend)
{ {
keys.hidden = true; keys.Hidden = true;
} }
this.enabledStage = 1; this.EnabledStage = 1;
break; break;
case 1 when this.keyboardExtend.Count > 0: case 1 when this.KeyboardExtend.Count > 0:
foreach (var keys in this.keyboardExtend) foreach (var keys in this.KeyboardExtend)
{ {
keys.hidden = false; keys.Hidden = false;
} }
this.enabledStage = 2; this.EnabledStage = 2;
break; break;
default: default:
foreach (var keys in this.keyboard) foreach (var keys in this.Keyboard)
{ {
keys.hidden = true; keys.Hidden = true;
} }
foreach (var keys in this.keyboardExtend) foreach (var keys in this.KeyboardExtend)
{ {
keys.hidden = true; keys.Hidden = true;
} }
this.enabledStage = 0; this.EnabledStage = 0;
if (Game1.activeClickableMenu is IClickableMenu menu && !(Game1.activeClickableMenu is DialogueBox)) if (Game1.activeClickableMenu is IClickableMenu menu && !(Game1.activeClickableMenu is DialogueBox))
{ {
menu.exitThisMenu(); menu.exitThisMenu();
@ -119,16 +119,16 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
} }
} }
private bool shouldTrigger(Vector2 screenPixels) private bool ShouldTrigger(Vector2 screenPixels)
{ {
int tick = Game1.ticks; int tick = Game1.ticks;
if(tick - this.lastPressTick <= 6) if(tick - this.LastPressTick <= 6)
{ {
return false; return false;
} }
if (this.virtualToggleButton.containsPoint((int)(screenPixels.X * Game1.options.zoomLevel), (int)(screenPixels.Y * Game1.options.zoomLevel))) if (this.VirtualToggleButton.containsPoint((int)(screenPixels.X * Game1.options.zoomLevel), (int)(screenPixels.Y * Game1.options.zoomLevel)))
{ {
this.lastPressTick = tick; this.LastPressTick = tick;
Toolbar.toolbarPressed = true; Toolbar.toolbarPressed = true;
return true; return true;
} }
@ -137,42 +137,43 @@ namespace StardewModdingAPI.Mods.VirtualKeyboard
private void OnRendered(object sender, EventArgs e) private void OnRendered(object sender, EventArgs e)
{ {
if (this.isDefault) if (this.IsDefault)
{ {
if (Game1.options.verticalToolbar) if (Game1.options.verticalToolbar)
this.virtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 200; this.VirtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 200;
else else
this.virtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 50; this.VirtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 50;
if (Game1.toolbar.alignTop == true && !Game1.options.verticalToolbar) if (Game1.toolbar.alignTop == true && !Game1.options.verticalToolbar)
{ {
object toolbarHeight = this.helper.Reflection.GetField<int>(Game1.toolbar, "toolbarHeight").GetValue(); object toolbarHeight = this.Helper.Reflection.GetField<int>(Game1.toolbar, "toolbarHeight").GetValue();
this.virtualToggleButton.bounds.Y = (int)toolbarHeight + 50; this.VirtualToggleButton.bounds.Y = (int)toolbarHeight + 50;
} }
else else
{ {
this.virtualToggleButton.bounds.Y = 12; this.VirtualToggleButton.bounds.Y = 12;
} }
} }
else else
{ {
this.virtualToggleButton.bounds.X = this.modConfig.vToggle.rectangle.X; this.VirtualToggleButton.bounds.X = this.ModConfig.vToggle.rectangle.X;
this.virtualToggleButton.bounds.Y = this.modConfig.vToggle.rectangle.Y; this.VirtualToggleButton.bounds.Y = this.ModConfig.vToggle.rectangle.Y;
} }
float scale = 1f; float scale = 1f;
if (this.enabledStage == 0) if (this.EnabledStage == 0)
{ {
scale = 0.5f; scale = 0.5f;
} }
if (!Game1.eventUp && Game1.activeClickableMenu is GameMenu == false && Game1.activeClickableMenu is ShopMenu == false) if (!Game1.eventUp && Game1.activeClickableMenu is GameMenu == false && Game1.activeClickableMenu is ShopMenu == false)
scale = 0.25f; scale = 0.25f;
System.Reflection.FieldInfo matrixField = Game1.spriteBatch.GetType().GetField("_matrix", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); System.Reflection.FieldInfo spriteEffectField = Game1.spriteBatch.GetType().GetField("_spriteEffect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
object originMatrix = matrixField.GetValue(Game1.spriteBatch); SpriteEffect originSpriteEffect = spriteEffectField?.GetValue(Game1.spriteBatch) as SpriteEffect;
var originMatrix = originSpriteEffect?.TransformMatrix;
Game1.spriteBatch.End(); Game1.spriteBatch.End();
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, Microsoft.Xna.Framework.Matrix.CreateScale(1f)); Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, Microsoft.Xna.Framework.Matrix.CreateScale(1f));
this.virtualToggleButton.draw(Game1.spriteBatch, Color.White * scale, 0.000001f); this.VirtualToggleButton.draw(Game1.spriteBatch, Color.White * scale, 0.000001f);
Game1.spriteBatch.End(); Game1.spriteBatch.End();
if (originMatrix != null) if (originMatrix != null)
{ {

View File

@ -1,8 +1,8 @@
{ {
"Name": "VirtualKeyboard", "Name": "VirtualKeyboard",
"Author": "SMAPI", "Author": "SMAPI",
"Version": "3.3.1", "Version": "4.0.1",
"MinimumApiVersion": "3.4.0", "MinimumApiVersion": "3.18.2",
"Description": "A much needed Virtual Keyboard for SMAPI Android.", "Description": "A much needed Virtual Keyboard for SMAPI Android.",
"UniqueID": "VirtualKeyboard", "UniqueID": "VirtualKeyboard",
"EntryDll": "VirtualKeyboard.dll", "EntryDll": "VirtualKeyboard.dll",

View File

@ -16,11 +16,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
HarmonyMethod finalizer = null) HarmonyMethod finalizer = null)
{ {
if (Constants.HarmonyEnabled) if (Constants.HarmonyEnabled)
#if HARMONY_2
return instance.Patch(original, prefix, postfix, transpiler, finalizer); return instance.Patch(original, prefix, postfix, transpiler, finalizer);
#else
return instance.Patch(original, prefix, postfix, transpiler);
#endif
else else
return null; return null;
} }

View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
{
/// <summary>Rewrites all references to a type.</summary>
internal class ModuleReferenceRewriter : IInstructionHandler
{
/*********
** Accessors
*********/
/// <inheritdoc />
public string DefaultPhrase { get; }
/// <inheritdoc />
public ISet<InstructionHandleResult> Flags { get; } = new HashSet<InstructionHandleResult>();
/// <inheritdoc />
public ISet<string> Phrases { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly string AssemblyName;
private readonly AssemblyNameReference Target;
public ModuleReferenceRewriter(string phrase, string assemblyName, Assembly target)
{
this.DefaultPhrase = $"{phrase} assembly ref";
this.AssemblyName = assemblyName;
this.Target = AssemblyNameReference.Parse(target.FullName);
}
public bool Handle(ModuleDefinition module)
{
if (!module.AssemblyReferences.Any(assembly => assembly.Name.Equals(this.AssemblyName)))
{
return false;
}
// add target assembly references
module.AssemblyReferences.Add(this.Target);
// rewrite type scopes to use target assemblies
IEnumerable<TypeReference> typeReferences = module.GetTypeReferences()
.Where(p => p.Scope.Name.Equals(this.AssemblyName))
.OrderBy(p => p.FullName);
foreach (TypeReference type in typeReferences)
type.Scope = this.Target;
// rewrite types using custom attributes
foreach (TypeDefinition type in module.GetTypes())
{
foreach (CustomAttribute attr in type.CustomAttributes)
{
foreach (CustomAttributeArgument conField in attr.ConstructorArguments)
{
if (conField.Value is TypeReference typeRef)
typeRef.Scope = this.Target;
}
}
}
for (int i = module.AssemblyReferences.Count - 1; i >= 0; i--)
{
if(module.AssemblyReferences[i].Name.Equals(this.AssemblyName))
{
module.AssemblyReferences.RemoveAt(i);
}
}
this.Flags.Add(InstructionHandleResult.Rewritten);
return true;
}
public bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
return false;
}
public bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
return false;
}
}
}

View File

@ -53,26 +53,14 @@ namespace StardewModdingAPI.Metadata
yield return new FieldReplaceRewriter() yield return new FieldReplaceRewriter()
.AddField(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture)) .AddField(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture))
.AddField(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps)) .AddField(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps))
#if SMAPI_FOR_MOBILE
.AddField(typeof(ItemGrabMenu), "context", typeof(ItemGrabMenu), "specialObject")
#endif
.AddField(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps)); .AddField(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
#if SMAPI_FOR_MOBILE #if SMAPI_FOR_MOBILE
#if SMAPI_LEGACY_PATCH // module rewrite for .Net 5 runtime assemblies
// Redirect reference yield return new ModuleReferenceRewriter("System.Collections", "System.Collections", typeof(System.Collections.CollectionBase).Assembly);
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isRaining", nameof(Game1Methods.IsRainingProp)); yield return new ModuleReferenceRewriter("System.Runtime", "System.Runtime", typeof(System.Collections.CollectionBase).Assembly);
#if !ANDROID_TARGET_MOBILE_LEGACY
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isSnowing", nameof(Game1Methods.IsSnowingProp));
#endif
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isDebrisWeather", nameof(Game1Methods.IsDebrisWeatherProp));
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "rainDrops", nameof(Game1Methods.RainDropsProp));
// yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(WeatherDebrisManager), "debrisWeather","weatherDebrisList", "Instance");
#endif
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "onScreenMenus", "onScreenMenus"); yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "onScreenMenus", "onScreenMenus");
yield return new PropertyToFieldRewriter(typeof(Game1), "toolSpriteSheet", "toolSpriteSheet");
// yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(GameLocation), typeof(DebrisManager), "debris", this.Monitor, "debrisNetCollection");
// Menu fix // Menu fix
yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(MenuWithInventory), typeof(MenuWithInventoryMethods), "trashCan", nameof(MenuWithInventoryMethods.TrashCanProp)); yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(MenuWithInventory), typeof(MenuWithInventoryMethods), "trashCan", nameof(MenuWithInventoryMethods.TrashCanProp));

View File

@ -4,3 +4,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moq for unit testing [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moq for unit testing
[assembly: InternalsVisibleTo("ContentPatcher")] [assembly: InternalsVisibleTo("ContentPatcher")]
[assembly: InternalsVisibleTo("ErrorHandler")] [assembly: InternalsVisibleTo("ErrorHandler")]
#if SMAPI_FOR_MOBILE
[assembly: InternalsVisibleTo("VirtualKeyboard")]
#endif