diff --git a/src/SMAPI.sln b/src/SMAPI.sln index 316ca494..aa4e23a8 100644 --- a/src/SMAPI.sln +++ b/src/SMAPI.sln @@ -73,6 +73,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StardewModdingAPI", "StardewModdingAPI\StardewModdingAPI.csproj", "{9898B56E-51EB-40CF-8B1F-ACEB4B6397A7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StardewModdingAPI.Mods.VirtualKeyboard", "StardewModdingAPI.Mods.VirtualKeyboard\StardewModdingAPI.Mods.VirtualKeyboard.csproj", "{29CCE9C9-6811-415D-A681-A6D47073924D}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution SMAPI.Internal\SMAPI.Internal.projitems*{443ddf81-6aaf-420a-a610-3459f37e5575}*SharedItemsImports = 4 @@ -129,6 +131,10 @@ Global {9898B56E-51EB-40CF-8B1F-ACEB4B6397A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9898B56E-51EB-40CF-8B1F-ACEB4B6397A7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9898B56E-51EB-40CF-8B1F-ACEB4B6397A7}.Release|Any CPU.Build.0 = Release|Any CPU + {29CCE9C9-6811-415D-A681-A6D47073924D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29CCE9C9-6811-415D-A681-A6D47073924D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29CCE9C9-6811-415D-A681-A6D47073924D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29CCE9C9-6811-415D-A681-A6D47073924D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/KeyButton.cs b/src/StardewModdingAPI.Mods.VirtualKeyboard/KeyButton.cs new file mode 100644 index 00000000..b07f1b2c --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/KeyButton.cs @@ -0,0 +1,145 @@ +using System; +using Microsoft.Xna.Framework; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.Menus; +using static StardewModdingAPI.Mods.VirtualKeyboard.ModConfig; +using System.Reflection; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI.Mods.VirtualKeyboard +{ + class KeyButton + { + private readonly IModHelper helper; + private readonly IMonitor Monitor; + private readonly Rectangle buttonRectangle; + private readonly int padding; + + private readonly IReflectedMethod RaiseButtonPressed; + private readonly IReflectedMethod RaiseButtonReleased; + private readonly IReflectedMethod Legacy_KeyPressed; + private readonly IReflectedMethod Legacy_KeyReleased; + + private readonly SButton button; + private readonly float transparency; + public bool hidden; + private bool raisingPressed = false; + private bool raisingReleased = false; + + public KeyButton(IModHelper helper, VirtualButton buttonDefine, IMonitor monitor) + { + this.Monitor = monitor; + this.helper = helper; + this.hidden = true; + this.buttonRectangle = new Rectangle(buttonDefine.rectangle.X, buttonDefine.rectangle.Y, buttonDefine.rectangle.Width, buttonDefine.rectangle.Height); + this.padding = buttonDefine.rectangle.Padding; + this.button = buttonDefine.key; + if (buttonDefine.transparency <= 0.01f || buttonDefine.transparency > 1f) + { + buttonDefine.transparency = 0.5f; + } + this.transparency = buttonDefine.transparency; + + helper.Events.Display.RenderingHud += this.OnRenderingHud; + helper.Events.Input.ButtonReleased += this.EventInputButtonReleased; + helper.Events.Input.ButtonPressed += this.EventInputButtonPressed; + + //TODO + //Use C# Reflection and re-enable SMAPI IReflected checks + + MainActivity activity = this.helper.Reflection.GetField(typeof(MainActivity), "instance").GetValue(); + object score = activity.GetType().GetField("core", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(activity); + object eventManager = score.GetType().GetField("EventManager", BindingFlags.Public | BindingFlags.Instance).GetValue(score); + + object buttonPressed = eventManager.GetType().GetField("ButtonPressed", BindingFlags.Public | BindingFlags.Instance).GetValue(eventManager); + object buttonReleased = eventManager.GetType().GetField("ButtonReleased", BindingFlags.Public | BindingFlags.Instance).GetValue(eventManager); + + object legacyButtonPressed = eventManager.GetType().GetField("Legacy_KeyPressed", BindingFlags.Public | BindingFlags.Instance).GetValue(eventManager); + object legacyButtonReleased = eventManager.GetType().GetField("Legacy_KeyReleased", BindingFlags.Public | BindingFlags.Instance).GetValue(eventManager); + + this.RaiseButtonPressed = this.helper.Reflection.GetMethod(buttonPressed, "Raise"); + this.RaiseButtonReleased = this.helper.Reflection.GetMethod(buttonReleased, "Raise"); + + this.Legacy_KeyPressed = this.helper.Reflection.GetMethod(legacyButtonPressed, "Raise"); + this.Legacy_KeyReleased = this.helper.Reflection.GetMethod(legacyButtonReleased, "Raise"); + } + + private bool shouldTrigger(Vector2 point) + { + int x1 = Mouse.GetState().X / (int)Game1.NativeZoomLevel; + int y1 = Mouse.GetState().Y / (int)Game1.NativeZoomLevel; + if (this.buttonRectangle.Contains(x1, y1)) + { + return true; + } + return false; + } + + private void EventInputButtonPressed(object sender, ButtonPressedEventArgs e) + { + if (this.raisingPressed) + { + return; + } + Vector2 point = e.Cursor.ScreenPixels; + if (this.shouldTrigger(point)) + { + object inputState = e.GetType().GetField("InputState", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(e); + + object buttonPressedEventArgs = Activator.CreateInstance(typeof(ButtonPressedEventArgs), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { this.button, e.Cursor, inputState }, null); + EventArgsKeyPressed eventArgsKey = new EventArgsKeyPressed((Keys)this.button); + try + { + this.raisingPressed = true; + + //METHODBASE.INVOKE Method + //this.RaiseButtonPressed.Invoke("What goes here???", new object[] { buttonPressedEventArgs }); + + this.RaiseButtonPressed.Invoke(new object[] { buttonPressedEventArgs }); + this.Legacy_KeyPressed.Invoke(new object[] { eventArgsKey }); + } + finally + { + this.raisingPressed = false; + } + } + } + + private void EventInputButtonReleased(object sender, ButtonReleasedEventArgs e) + { + if (this.raisingReleased) + { + return; + } + Vector2 point = e.Cursor.ScreenPixels; + if (this.shouldTrigger(point)) + { + object inputState = this.helper.Reflection.GetField(e, "InputState").GetValue(); + object buttonReleasedEventArgs = Activator.CreateInstance(typeof(ButtonReleasedEventArgs), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { this.button, e.Cursor, inputState }, null); + EventArgsKeyPressed eventArgsKeyReleased = new EventArgsKeyPressed((Keys)this.button); + try + { + this.raisingReleased = true; + this.RaiseButtonReleased.Invoke(new object[] { buttonReleasedEventArgs }); + this.Legacy_KeyReleased.Invoke(new object[] { eventArgsKeyReleased }); + } + finally + { + this.raisingReleased = false; + } + } + } + + /// Raised before drawing the HUD (item toolbar, clock, etc) to the screen. + /// The event sender. + /// The event arguments. + private void OnRenderingHud(object sender, EventArgs e) + { + if (!Game1.eventUp && !this.hidden) + { + IClickableMenu.drawButtonWithText(Game1.spriteBatch, Game1.smallFont, this.button.ToString(), this.buttonRectangle.X, this.buttonRectangle.Y, this.buttonRectangle.Width, this.buttonRectangle.Height, Color.BurlyWood * this.transparency); + } + } + } +} diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/ModConfig.cs b/src/StardewModdingAPI.Mods.VirtualKeyboard/ModConfig.cs new file mode 100644 index 00000000..79cbd0d2 --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/ModConfig.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using StardewValley; + +namespace StardewModdingAPI.Mods.VirtualKeyboard +{ + class ModConfig + { + public VirtualButton[] buttons { get; set; } = new VirtualButton[] { + new VirtualButton(SButton.Q, new Rect(192, 125, 90, 90, 6), 0.5f), + new VirtualButton(SButton.I, new Rect(288, 125, 90, 90, 6), 0.5f), + new VirtualButton(SButton.O, new Rect(384, 125, 90, 90, 6), 0.5f), + new VirtualButton(SButton.P, new Rect(480, 125, 90, 90, 6), 0.5f) + }; + internal class VirtualButton + { + public SButton key; + public Rect rectangle; + public float transparency; + public VirtualButton(SButton key, Rect rectangle, float transparency) + { + this.key = key; + this.rectangle = rectangle; + this.transparency = transparency; + } + } + internal class Rect + { + public int X; + public int Y; + public int Width; + public int Height; + public int Padding; + + public Rect(int x, int y, int width, int height, int padding) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + this.Padding = padding; + } + } + } +} diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/ModEntry.cs b/src/StardewModdingAPI.Mods.VirtualKeyboard/ModEntry.cs new file mode 100644 index 00000000..1aaf1db6 --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/ModEntry.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using StardewModdingAPI; +using StardewValley; +using StardewValley.Tools; + +namespace StardewModdingAPI.Mods.VirtualKeyboard +{ + public class ModEntry : Mod + { + //private List keyboard = new List(); + //private ModConfig modConfig; + public override void Entry(IModHelper helper) + { + VirtualToggle virtualToggle = new VirtualToggle(helper, this.Monitor); + //this.modConfig = helper.ReadConfig(); + //for (int i = 0; i < this.modConfig.buttons.Length; i++) + //{ + // this.keyboard.Add(new KeyButton(helper, this.modConfig.buttons[i], this.Monitor)); + //} + //helper.WriteConfig(this.modConfig); + } + } +} diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/Properties/AssemblyInfo.cs b/src/StardewModdingAPI.Mods.VirtualKeyboard/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..ae750e04 --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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")] diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/StardewModdingAPI.Mods.VirtualKeyboard.csproj b/src/StardewModdingAPI.Mods.VirtualKeyboard/StardewModdingAPI.Mods.VirtualKeyboard.csproj new file mode 100644 index 00000000..b94b1609 --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/StardewModdingAPI.Mods.VirtualKeyboard.csproj @@ -0,0 +1,94 @@ + + + + + Debug + AnyCPU + {29CCE9C9-6811-415D-A681-A6D47073924D} + Library + Properties + StardewModdingAPI.Mods.VirtualKeyboard + VirtualKeyboard + v4.5 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\Downloads\Stardew-Valley-v1-25_mod\unknown\assemblies\Mono.Android.dll + + + ..\..\..\..\..\Downloads\Stardew-Valley-v1-25_mod\unknown\assemblies\MonoGame.Framework.dll + + + ..\..\..\..\..\Downloads\Stardew-Valley-v1-25_mod\unknown\assemblies\StardewValley.dll + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + + + True + + + True + + + + + + + + + + + + {9898b56e-51eb-40cf-8b1f-aceb4b6397a7} + StardewModdingAPI + + + + + + + + + + \ No newline at end of file diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/VirtualToggle.cs b/src/StardewModdingAPI.Mods.VirtualKeyboard/VirtualToggle.cs new file mode 100644 index 00000000..0e21503c --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/VirtualToggle.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.Locations; +using StardewValley.Menus; +using StardewValley.Mobile; + +namespace StardewModdingAPI.Mods.VirtualKeyboard +{ + class VirtualToggle + { + private readonly IModHelper helper; + private readonly IMonitor Monitor; + + private bool enabled = false; + private ClickableTextureComponent virtualToggleButton; + + private List keyboard = new List(); + private ModConfig modConfig; + private Texture2D texture; + + public VirtualToggle(IModHelper helper, IMonitor monitor) + { + this.Monitor = monitor; + this.helper = helper; + this.texture = this.helper.Content.Load("assets/togglebutton.png", ContentSource.ModFolder); + this.virtualToggleButton = new ClickableTextureComponent(new Rectangle(Game1.toolbarPaddingX + 36, 12, 64, 64), this.texture, new Rectangle(0, 0, 16, 16), 5.75f, false); + + this.modConfig = helper.ReadConfig(); + for (int i = 0; i < this.modConfig.buttons.Length; i++) + { + this.keyboard.Add(new KeyButton(helper, this.modConfig.buttons[i], this.Monitor)); + } + helper.WriteConfig(this.modConfig); + + this.helper.Events.Display.RenderingHud += this.OnRenderingHUD; + this.helper.Events.Input.ButtonPressed += this.VirtualToggleButtonPressed; + this.helper.Events.Input.ButtonReleased += this.VirtualToggleButtonReleased; + } + + private void VirtualToggleButtonPressed(object sender, ButtonPressedEventArgs e) + { + if (!this.enabled && this.shouldTrigger()) + { + this.enabled = true; + foreach (var keys in this.keyboard) + { + keys.hidden = false; + } + } + else if (this.enabled && this.shouldTrigger()) + { + this.enabled = false; + foreach (var keys in this.keyboard) + { + keys.hidden = true; + } + } + } + + private bool shouldTrigger() + { + int x1 = Mouse.GetState().X / (int)Game1.NativeZoomLevel; + int y1 = Mouse.GetState().Y / (int)Game1.NativeZoomLevel; + if (this.virtualToggleButton.containsPoint(x1, y1)) + { + return true; + } + return false; + } + + private void VirtualToggleButtonReleased(object sender, ButtonReleasedEventArgs e) + { + } + + private void OnRenderingHUD(object sender, EventArgs e) + { + if (Game1.options.verticalToolbar) + this.virtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 150; + else + this.virtualToggleButton.bounds.X = Game1.toolbarPaddingX + Game1.toolbar.itemSlotSize + 50; + this.virtualToggleButton.bounds.Y = 10; + float scale = 1f; + if (!this.enabled) + { + scale = 0.5f; + } + if(!Game1.eventUp) + this.virtualToggleButton.draw(Game1.spriteBatch, Color.White * scale, 0f); + } + } +} diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/assets/togglebutton.png b/src/StardewModdingAPI.Mods.VirtualKeyboard/assets/togglebutton.png new file mode 100644 index 00000000..abce001d Binary files /dev/null and b/src/StardewModdingAPI.Mods.VirtualKeyboard/assets/togglebutton.png differ diff --git a/src/StardewModdingAPI.Mods.VirtualKeyboard/manifest.json b/src/StardewModdingAPI.Mods.VirtualKeyboard/manifest.json new file mode 100644 index 00000000..fa598c32 --- /dev/null +++ b/src/StardewModdingAPI.Mods.VirtualKeyboard/manifest.json @@ -0,0 +1,10 @@ +{ + "Name": "VirtualKeyboard", + "Author": "MartyrPher", + "Version": "1.0.0", + "MinimumApiVersion": "2.10.1", + "Description": "A much needed Virtual Keyboard for SMAPI Android.", + "UniqueID": "VirtualKeyboard", + "EntryDll": "VirtualKeyboard.dll", + "UpdateKeys": [ "Nexus: null" ] +} diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index fedc4ce4..355a9fed 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -166,7 +166,11 @@ namespace StardewModdingAPI targetAssemblies = new[] { typeof(StardewValley.Game1).Assembly, // note: includes Netcode types on Linux/Mac - typeof(Microsoft.Xna.Framework.Vector2).Assembly + typeof(Microsoft.Xna.Framework.Vector2).Assembly, + typeof(MonoMod.RuntimeDetour.HarmonyDetourBridge).Assembly, + typeof(MonoMod.Utils.Platform).Assembly, + typeof(Harmony.HarmonyPatch).Assembly, + typeof(Mono.Cecil.MethodDefinition).Assembly, }; break; diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs index 0ce72a9e..e66f2cb0 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs @@ -39,9 +39,10 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the field is not found. public IReflectedField GetField(object obj, string name, bool required = true) { - return this.AssertAccessAllowed( - this.Reflector.GetField(obj, name, required) - ); + //return this.AssertAccessAllowed( + // this.Reflector.GetField(obj, name, required) + //); + return this.Reflector.GetField(obj, name, required); } /// Get a static field. @@ -51,9 +52,10 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the field is not found. public IReflectedField GetField(Type type, string name, bool required = true) { - return this.AssertAccessAllowed( - this.Reflector.GetField(type, name, required) - ); + //return this.AssertAccessAllowed( + // this.Reflector.GetField(type, name, required) + //); + return this.Reflector.GetField(type, name, required); } /// Get an instance property. @@ -86,9 +88,10 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the field is not found. public IReflectedMethod GetMethod(object obj, string name, bool required = true) { - return this.AssertAccessAllowed( - this.Reflector.GetMethod(obj, name, required) - ); + //return this.AssertAccessAllowed( + // this.Reflector.GetMethod(obj, name, required) + //); + return this.Reflector.GetMethod(obj, name, required); } /// Get a static method. @@ -97,9 +100,10 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the field is not found. public IReflectedMethod GetMethod(Type type, string name, bool required = true) { - return this.AssertAccessAllowed( - this.Reflector.GetMethod(type, name, required) - ); + //return this.AssertAccessAllowed( + // this.Reflector.GetMethod(type, name, required) + //); + return this.Reflector.GetMethod(type, name, required); } diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs index 878b3148..d14ea5e5 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs @@ -281,7 +281,7 @@ namespace StardewModdingAPI.Framework.ModLoading // find (and optionally rewrite) incompatible instructions bool anyRewritten = false; - IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers(this.ParanoidMode).ToArray(); + IInstructionHandler[] handlers = new InstructionMetadata(this.Monitor).GetHandlers(this.ParanoidMode).ToArray(); foreach (MethodDefinition method in this.GetMethods(module)) { // check method definition diff --git a/src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs b/src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs index d4df22fb..a2d936a2 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs @@ -16,6 +16,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /// The type whose field to which references should be rewritten to. private readonly Type ToType; + /// The field name. + private readonly string FieldName; + /// The property name. private readonly string PropertyName; @@ -28,12 +31,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /// The type whose field to which references should be rewritten. /// The field name to rewrite. /// The property name (if different). - public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, string propertyName) + public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, string propertyName, IMonitor monitor) : base(type.FullName, fieldName, InstructionHandleResult.None) { - //this.Monitor = monitor; + this.Monitor = monitor; this.Type = type; this.ToType = toType; + this.FieldName = fieldName; this.PropertyName = propertyName; } @@ -41,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /// The type whose field to which references should be rewritten. /// The field name to rewrite. public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, IMonitor monitor) - : this(type, toType, fieldName, fieldName) { } + : this(type, toType, fieldName, fieldName, monitor) { } /// Perform the predefined logic for an instruction if applicable. /// The assembly module containing the instruction. @@ -54,23 +58,21 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters if (!this.IsMatch(instruction)) return InstructionHandleResult.None; - //Instruction: IL_0025: ldsfld StardewValley.GameLocation StardewValley.Game1::currentLocation string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; try { - //MethodReference propertyRef = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}")); - MethodReference method = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}")); - this.Monitor.Log("Method Ref: " + method.ToString()); + FieldReference field = module.ImportReference(this.ToType.GetField(this.FieldName)); + cil.InsertAfter(instruction, cil.Create(OpCodes.Ldfld, field)); cil.Replace(instruction, cil.Create(OpCodes.Call, method)); } catch (Exception e) { - //this.Monitor.Log(e.Message); + this.Monitor.Log(e.Message); + this.Monitor.Log(e.StackTrace); } - return InstructionHandleResult.Rewritten; } } diff --git a/src/StardewModdingAPI/Framework/Patching/GamePatcher.cs b/src/StardewModdingAPI/Framework/Patching/GamePatcher.cs index 2ba485d1..8aaae5c9 100644 --- a/src/StardewModdingAPI/Framework/Patching/GamePatcher.cs +++ b/src/StardewModdingAPI/Framework/Patching/GamePatcher.cs @@ -1,4 +1,5 @@ using System; +using Android.OS; using Harmony; using MonoMod.RuntimeDetour; @@ -28,21 +29,24 @@ namespace StardewModdingAPI.Framework.Patching /// The patches to apply. public void Apply(params IHarmonyPatch[] patches) { - HarmonyDetourBridge.Init(); - - HarmonyInstance harmony = HarmonyInstance.Create("io.smapi"); - foreach (IHarmonyPatch patch in patches) + if(Build.VERSION.SdkInt > BuildVersionCodes.LollipopMr1) { - try + HarmonyDetourBridge.Init(); + + HarmonyInstance harmony = HarmonyInstance.Create("io.smapi"); + foreach (IHarmonyPatch patch in patches) { - patch.Apply(harmony); + try + { + patch.Apply(harmony); + } + catch (Exception ex) + { + this.Monitor.Log($"Couldn't apply runtime patch '{patch.Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error); + this.Monitor.Log(ex.GetLogSummary(), LogLevel.Trace); + } } - catch (Exception ex) - { - this.Monitor.Log($"Couldn't apply runtime patch '{patch.Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error); - this.Monitor.Log(ex.GetLogSummary(), LogLevel.Trace); - } - } + } } } } diff --git a/src/StardewModdingAPI/Framework/RewriteFacades/FarmerMethods.cs b/src/StardewModdingAPI/Framework/RewriteFacades/FarmerMethods.cs index 0a8c4dad..b9bbf388 100644 --- a/src/StardewModdingAPI/Framework/RewriteFacades/FarmerMethods.cs +++ b/src/StardewModdingAPI/Framework/RewriteFacades/FarmerMethods.cs @@ -17,5 +17,11 @@ namespace StardewModdingAPI.Framework.RewriteFacades { return base.addItemToInventoryBool(item, false); } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new int freeSpotsInInventory() + { + return base.freeSpotsInInventory(null); + } } } diff --git a/src/StardewModdingAPI/Framework/SCore.cs b/src/StardewModdingAPI/Framework/SCore.cs index 491a85fb..bcaec6df 100644 --- a/src/StardewModdingAPI/Framework/SCore.cs +++ b/src/StardewModdingAPI/Framework/SCore.cs @@ -76,7 +76,7 @@ namespace StardewModdingAPI.Framework private readonly ModRegistry ModRegistry = new ModRegistry(); /// Manages SMAPI events for mods. - private readonly EventManager EventManager; + public readonly EventManager EventManager; /// Whether the game is currently running. private bool IsGameRunning; @@ -296,7 +296,7 @@ namespace StardewModdingAPI.Framework this.Monitor.Log("The game crashed last time you played. That can be due to bugs in the game, but if it happens repeatedly you can ask for help here: https://community.playstarbound.com/threads/108375/.", LogLevel.Error); this.Monitor.Log("If you ask for help, make sure to share your SMAPI log: https://log.smapi.io.", LogLevel.Error); this.Monitor.Log("Press any key to delete the crash data and continue playing.", LogLevel.Info); - Console.ReadKey(); + //Console.ReadKey(); File.Delete(Constants.FatalCrashLog); File.Delete(Constants.FatalCrashMarker); } diff --git a/src/StardewModdingAPI/IReflectedMethod.cs b/src/StardewModdingAPI/IReflectedMethod.cs index de83b98c..17ceaccd 100644 --- a/src/StardewModdingAPI/IReflectedMethod.cs +++ b/src/StardewModdingAPI/IReflectedMethod.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; namespace StardewModdingAPI { diff --git a/src/StardewModdingAPI/Metadata/InstructionMetadata.cs b/src/StardewModdingAPI/Metadata/InstructionMetadata.cs index 108271bb..9f29aae2 100644 --- a/src/StardewModdingAPI/Metadata/InstructionMetadata.cs +++ b/src/StardewModdingAPI/Metadata/InstructionMetadata.cs @@ -20,6 +20,13 @@ namespace StardewModdingAPI.Metadata /// The current implementation only works correctly with assemblies that should always be present. private readonly string[] ValidateReferencesToAssemblies = { "StardewModdingAPI", "Stardew Valley", "StardewValley", "Netcode" }; + private readonly IMonitor Monitor; + + public InstructionMetadata(IMonitor monitor) + { + this.Monitor = monitor; + } + /********* ** Public methods @@ -42,9 +49,12 @@ namespace StardewModdingAPI.Metadata //Constructor Rewrites yield return new MethodParentRewriter(typeof(HUDMessage), typeof(HUDMessageMethods)); - yield return new MethodParentRewriter(typeof(MapPageMethods), typeof(MapPageMethods)); + yield return new MethodParentRewriter(typeof(MapPage), typeof(MapPageMethods)); yield return new MethodParentRewriter(typeof(TextBox), typeof(TextBoxMethods)); + //Field Rewriters + yield return new FieldReplaceRewriter(typeof(ItemGrabMenu), "context", "specialObject"); + // rewrite for Stardew Valley 1.3 yield return new StaticFieldToConstantRewriter(typeof(Game1), "tileSize", Game1.tileSize); yield return new TypeReferenceRewriter("System.Collections.Generic.IList`1", typeof(List)); @@ -58,8 +68,8 @@ namespace StardewModdingAPI.Metadata yield return new FieldToPropertyRewriter(typeof(Game1), "stats"); //isRaining and isDebrisWeather fix 50% done. - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(RainManager), "isRaining", "Instance"); - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(DebrisManager), "isDebrisWeather", "Instance"); + yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(RainManager), "isRaining", "Instance", this.Monitor); + yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(WeatherDebrisManager), "isDebrisWeather", "Instance", this.Monitor); /**** ** detect mod issues diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 3211f009..d62077a2 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -7,6 +7,7 @@ using System.Threading; #if SMAPI_FOR_WINDOWS #endif using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.ModLoading; using StardewModdingAPI.Internal; namespace StardewModdingAPI @@ -52,7 +53,6 @@ namespace StardewModdingAPI { if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.InvariantCultureIgnoreCase)) { - Android.Util.Log.Error("Program", $"Resolving assembly: {dll.FullName}"); return Assembly.LoadFrom(dll.FullName); } diff --git a/src/StardewModdingAPI/SMainActivity.cs b/src/StardewModdingAPI/SMainActivity.cs index feea1edc..f342c070 100644 --- a/src/StardewModdingAPI/SMainActivity.cs +++ b/src/StardewModdingAPI/SMainActivity.cs @@ -25,6 +25,7 @@ using System.Threading.Tasks; using StardewModdingAPI.Framework; using StardewValley; using Android.Widget; +using StardewModdingAPI.Framework.ModLoading; namespace StardewModdingAPI { @@ -130,11 +131,14 @@ namespace StardewModdingAPI { get { - if (ContextCompat.CheckSelfPermission(this, "android.permission.ACCESS_NETWORK_STATE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.ACCESS_NETWORK_STATE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.ACCESS_WIFI_STATE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.INTERNET") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.READ_EXTERNAL_STORAGE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.VIBRATE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.WAKE_LOCK") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") == Permission.Granted && ContextCompat.CheckSelfPermission(this, "com.android.vending.CHECK_LICENSE") == Permission.Granted) - { - return true; - } - return false; + return this.PackageManager.CheckPermission("android.permission.ACCESS_NETWORK_STATE", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.ACCESS_WIFI_STATE", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.INTERNET", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.READ_EXTERNAL_STORAGE", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.VIBRATE", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.WAKE_LOCK", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("android.permission.WRITE_EXTERNAL_STORAGE", this.PackageName) == Permission.Granted + && this.PackageManager.CheckPermission("com.android.vending.CHECK_LICENSE", this.PackageName) == Permission.Granted; } } @@ -530,15 +534,199 @@ namespace StardewModdingAPI public void PromptForPermissionsIfNecessary(Action callback = null) { + //Log.It("MainActivity.PromptForPermissionsIfNecessary..."); if (this.HasPermissions) { - callback?.Invoke(); - return; + if (callback != null) + { + //Log.It("MainActivity.PromptForPermissionsIfNecessary has permissions, calling callback"); + callback(); + return; + } + } + else + { + //Log.It("MainActivity.PromptForPermissionsIfNecessary doesn't have permissions, prompt for them"); + this._callback = callback; + this.PromptForPermissionsWithReasonFirst(); } - this._callback = callback; - this.PromptForPermissions(); } + private void PromptForPermissionsWithReasonFirst() + { + //Log.It("MainActivity.PromptForPermissionsWithReasonFirst..."); + if (!this.HasPermissions) + { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + string languageCode = Locale.Default.Language.Substring(0, 2); + builder.SetMessage(this.PermissionMessageA(languageCode)); + builder.SetPositiveButton(this.GetOKString(languageCode), delegate (object senderAlert, DialogClickEventArgs args) + { + //Log.It("MainActivity.PromptForPermissionsWithReasonFirst PromptForPermissions A"); + this.PromptForPermissions(); + }); + Dialog dialog = builder.Create(); + if (!this.IsFinishing) + { + dialog.Show(); + return; + } + } + else + { + //Log.It("MainActivity.PromptForPermissionsWithReasonFirst PromptForPermissions B"); + this.PromptForPermissions(); + } + } + + private string GetOKString(string languageCode) + { + if (languageCode == "de") + { + return "OK"; + } + if (languageCode == "es") + { + return "DE ACUERDO"; + } + if (languageCode == "ja") + { + return "OK"; + } + if (languageCode == "pt") + { + return "Está bem"; + } + if (languageCode == "ru") + { + return "Хорошо"; + } + if (languageCode == "ko") + { + return "승인"; + } + if (languageCode == "tr") + { + return "tamam"; + } + if (languageCode == "fr") + { + return "D'accord"; + } + if (languageCode == "hu") + { + return "rendben"; + } + if (languageCode == "it") + { + return "ok"; + } + if (languageCode == "zh") + { + return "好"; + } + return "OK"; + } + + private string PermissionMessageA(string languageCode) + { + if (languageCode == "de") + { + return "Du musst die Erlaubnis zum Lesen/Schreiben auf dem externen Speicher geben, um das Spiel zu speichern und Speicherstände auf andere Plattformen übertragen zu können. Bitte gib diese Genehmigung, um spielen zu können."; + } + if (languageCode == "es") + { + return "Para guardar la partida y transferir partidas guardadas a y desde otras plataformas, se necesita permiso para leer/escribir en almacenamiento externo. Concede este permiso para poder jugar."; + } + if (languageCode == "ja") + { + return "外部機器への読み込み/書き出しの許可が、ゲームのセーブデータの保存や他プラットフォームとの双方向のデータ移行実行に必要です。プレイを続けるには許可をしてください。"; + } + if (languageCode == "pt") + { + return "Para salvar o jogo e transferir jogos salvos entre plataformas é necessário permissão para ler/gravar em armazenamento externo. Forneça essa permissão para jogar."; + } + if (languageCode == "ru") + { + return "Для сохранения игры и переноса сохранений с/на другие платформы нужно разрешение на чтение-запись на внешнюю память. Дайте разрешение, чтобы начать играть."; + } + if (languageCode == "ko") + { + return "게임을 저장하려면 외부 저장공간에 대한 읽기/쓰기 권한이 필요합니다. 또한 저장 데이터 이전 기능을 허용해 다른 플랫폼에서 게임 진행상황을 가져올 때에도 권한이 필요합니다. 게임을 플레이하려면 권한을 허용해 주십시오."; + } + if (languageCode == "tr") + { + return "Oyunu kaydetmek ve kayıtları platformlardan platformlara taşımak için harici depolamada okuma/yazma izni gereklidir. Lütfen oynayabilmek için izin verin."; + } + if (languageCode == "fr") + { + return "Une autorisation de lecture / écriture sur un stockage externe est requise pour sauvegarder le jeu et vous permettre de transférer des sauvegardes vers et depuis d'autres plateformes. Veuillez donner l'autorisation afin de jouer."; + } + if (languageCode == "hu") + { + return "A játék mentéséhez, és ahhoz, hogy a különböző platformok között hordozhasd a játékmentést, engedélyezned kell a külső tárhely olvasását/írását, Kérjük, a játékhoz engedélyezd ezeket."; + } + if (languageCode == "it") + { + return "È necessaria l'autorizzazione a leggere/scrivere su un dispositivo di memorizzazione esterno per salvare la partita e per consentire di trasferire i salvataggi da e su altre piattaforme. Concedi l'autorizzazione per giocare."; + } + if (languageCode == "zh") + { + return "《星露谷物语》请求获得授权用来保存游戏数据以及访问线上功能。"; + } + return "Read/write to external storage permission is required to save the game, and to allow to you transfer saves to and from other platforms. Please give permission in order to play."; + } + + private string PermissionMessageB(string languageCode) + { + if (languageCode == "de") + { + return "Bitte geh in die Handy-Einstellungen > Apps > Stardew Valley > Berechtigungen und aktiviere den Speicher, um das Spiel zu spielen."; + } + if (languageCode == "es") + { + return "En el teléfono, ve a Ajustes > Aplicaciones > Stardew Valley > Permisos y activa Almacenamiento para jugar al juego."; + } + if (languageCode == "ja") + { + return "設定 > アプリ > スターデューバレー > 許可の順に開いていき、ストレージを有効にしてからゲームをプレイしましょう。"; + } + if (languageCode == "pt") + { + return "Acesse Configurar > Aplicativos > Stardew Valley > Permissões e ative Armazenamento para jogar."; + } + if (languageCode == "ru") + { + return "Перейдите в меню Настройки > Приложения > Stardew Valley > Разрешения и дайте доступ к памяти, чтобы начать играть."; + } + if (languageCode == "ko") + { + return "휴대전화의 설정 > 어플리케이션 > 스타듀 밸리 > 권한 에서 저장공간을 활성화한 뒤 게임을 플레이해 주십시오."; + } + if (languageCode == "tr") + { + return "Lütfen oyunu oynayabilmek için telefonda Ayarlar > Uygulamalar > Stardew Valley > İzinler ve Depolamayı etkinleştir yapın."; + } + if (languageCode == "fr") + { + return "Veuillez aller dans les Paramètres du téléphone> Applications> Stardew Valley> Autorisations, puis activez Stockage pour jouer."; + } + if (languageCode == "hu") + { + return "Lépje be a telefonodon a Beállítások > Alkalmazások > Stardew Valley > Engedélyek menübe, majd engedélyezd a Tárhelyet a játékhoz."; + } + if (languageCode == "it") + { + return "Nel telefono, vai su Impostazioni > Applicazioni > Stardew Valley > Autorizzazioni e attiva Memoria archiviazione per giocare."; + } + if (languageCode == "zh") + { + return "可在“设置-权限隐私-按应用管理权限-星露谷物语”进行设置,并打开“电话”、“读取位置信息”、“存储”权限。"; + } + return "Please go into phone Settings > Apps > Stardew Valley > Permissions, and enable Storage to play the game."; + } + + private void LogPermissions() { //("MainActivity.LogPermissions , AccessNetworkState:" + ContextCompat.CheckSelfPermission(this, "android.permission.ACCESS_NETWORK_STATE") + ", AccessWifiState:" + ContextCompat.CheckSelfPermission(this, "android.permission.ACCESS_WIFI_STATE") + ", Internet:" + ContextCompat.CheckSelfPermission(this, "android.permission.INTERNET") + ", ReadExternalStorage:" + ContextCompat.CheckSelfPermission(this, "android.permission.READ_EXTERNAL_STORAGE") + ", Vibrate:" + ContextCompat.CheckSelfPermission(this, "android.permission.VIBRATE") + ", WakeLock:" + ContextCompat.CheckSelfPermission(this, "android.permission.WAKE_LOCK") + ", WriteExternalStorage:" + ContextCompat.CheckSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") + ", CheckLicense:" + ContextCompat.CheckSelfPermission(this, "com.android.vending.CHECK_LICENSE")); @@ -551,20 +739,15 @@ namespace StardewModdingAPI { //("MainActivity.CheckAppPermissions permissions already granted."); this.OnCreatePartTwo(); + return; } - else - { - this.PromptForPermissions(); - } + this.PromptForPermissionsWithReasonFirst(); } public void PromptForPermissions() { //("MainActivity.PromptForPermissions requesting permissions..."); - if (!this.IsFinishing) - { - ActivityCompat.RequestPermissions(this, this.deniedPermissionsArray, 0); - } + ActivityCompat.RequestPermissions(this, this.deniedPermissionsArray, 0); } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) @@ -574,62 +757,8 @@ namespace StardewModdingAPI { //("MainActivity.OnRequestPermissionsResult no permissions returned, RETURNING"); return; - } - string text = Java.Util.Locale.Default.Language.Substring(0, 2); - //("OnRequestPermissionsResult Language Code:" + text); - string message; - string message2; - switch (text) - { - case "de": - message = "Du musst die Erlaubnis zum Lesen/Schreiben auf dem externen Speicher geben, um das Spiel zu speichern und Speicherstände auf andere Plattformen übertragen zu können. Bitte gib diese Genehmigung, um spielen zu können."; - message2 = "Bitte geh in die Handy-Einstellungen > Apps > Stardew Valley > Berechtigungen und aktiviere den Speicher, um das Spiel zu spielen."; - break; - case "es": - message = "Para guardar la partida y transferir partidas guardadas a y desde otras plataformas, se necesita permiso para leer/escribir en almacenamiento externo. Concede este permiso para poder jugar."; - message2 = "En el teléfono, ve a Ajustes > Aplicaciones > Stardew Valley > Permisos y activa Almacenamiento para jugar al juego."; - break; - case "ja": - message = "外部機器への読み込み/書き出しの許可が、ゲ\u30fcムのセ\u30fcブデ\u30fcタの保存や他プラットフォ\u30fcムとの双方向のデ\u30fcタ移行実行に必要です。プレイを続けるには許可をしてください。"; - message2 = "設定 > アプリ > スタ\u30fcデュ\u30fcバレ\u30fc > 許可の順に開いていき、ストレ\u30fcジを有効にしてからゲ\u30fcムをプレイしましょう。"; - break; - case "pt": - message = "Para salvar o jogo e transferir jogos salvos entre plataformas é necessário permissão para ler/gravar em armazenamento externo. Forneça essa permissão para jogar."; - message2 = "Acesse Configurar > Aplicativos > Stardew Valley > Permissões e ative Armazenamento para jogar."; - break; - case "ru": - message = "Для сохранения игры и переноса сохранений с/на другие платформы нужно разрешение на чтение-запись на внешнюю память. Дайте разрешение, чтобы начать играть."; - message2 = "Перейдите в меню Настройки > Приложения > Stardew Valley > Разрешения и дайте доступ к памяти, чтобы начать играть."; - break; - case "ko": - message = "게임을 저장하려면 외부 저장공간에 대한 읽기/쓰기 권한이 필요합니다. 또한 저장 데이터 이전 기능을 허용해 다른 플랫폼에서 게임 진행상황을 가져올 때에도 권한이 필요합니다. 게임을 플레이하려면 권한을 허용해 주십시오."; - message2 = "휴대전화의 설정 > 어플리케이션 > 스타듀 밸리 > 권한 에서 저장공간을 활성화한 뒤 게임을 플레이해 주십시오."; - break; - case "tr": - message = "Oyunu kaydetmek ve kayıtları platformlardan platformlara taşımak için harici depolamada okuma/yazma izni gereklidir. Lütfen oynayabilmek için izin verin."; - message2 = "Lütfen oyunu oynayabilmek için telefonda Ayarlar > Uygulamalar > Stardew Valley > İzinler ve Depolamayı etkinleştir yapın."; - break; - case "fr": - message = "Une autorisation de lecture / écriture sur un stockage externe est requise pour sauvegarder le jeu et vous permettre de transférer des sauvegardes vers et depuis d'autres plateformes. Veuillez donner l'autorisation afin de jouer."; - message2 = "Veuillez aller dans les Paramètres du téléphone> Applications> Stardew Valley> Autorisations, puis activez Stockage pour jouer."; - break; - case "hu": - message = "A játék mentéséhez, és ahhoz, hogy a különböző platformok között hordozhasd a játékmentést, engedélyezned kell a külső tárhely olvasását/írását, Kérjük, a játékhoz engedélyezd ezeket."; - message2 = "Lépje be a telefonodon a Beállítások > Alkalmazások > Stardew Valley > Engedélyek menübe, majd engedélyezd a Tárhelyet a játékhoz."; - break; - case "it": - message = "È necessaria l'autorizzazione a leggere/scrivere su un dispositivo di memorizzazione esterno per salvare la partita e per consentire di trasferire i salvataggi da e su altre piattaforme. Concedi l'autorizzazione per giocare."; - message2 = "Nel telefono, vai su Impostazioni > Applicazioni > Stardew Valley > Autorizzazioni e attiva Memoria archiviazione per giocare."; - break; - case "zh": - message = "保存游戏进度,以及授权与其它平台交换游戏进度文件,都需要对外部存储器进行读 / 写的权限。要正常游戏,请授予权限。"; - message2 = "请转到手机的设置 > 应用 > Stardew Valley > 权限里,启用“存储”,以正常游戏。"; - break; - default: - message = "Read/write to external storage permission is required to save the game, and to allow to you transfer saves to and from other platforms. Please give permission in order to play."; - message2 = "Please go into phone Settings > Apps > Stardew Valley > Permissions, and enable Storage to play the game."; - break; - } + } + string languageCode = Locale.Default.Language.Substring(0, 2); int num = 0; if (requestCode == 0) { @@ -646,18 +775,19 @@ namespace StardewModdingAPI AlertDialog.Builder builder = new AlertDialog.Builder(this); if (ActivityCompat.ShouldShowRequestPermissionRationale(this, permissions[i])) { - builder.SetMessage(message); - builder.SetPositiveButton("OK", delegate + builder.SetMessage(this.PermissionMessageA(languageCode)); + builder.SetPositiveButton(this.GetOKString(languageCode), delegate (object senderAlert, DialogClickEventArgs args) { + //Log.It("MainActivity.OnRequestPermissionsResult PromptForPermissions D"); this.PromptForPermissions(); }); } else { - builder.SetMessage(message2); - builder.SetPositiveButton("OK", delegate + builder.SetMessage(this.PermissionMessageB(languageCode)); + builder.SetPositiveButton(this.GetOKString(languageCode), delegate (object senderAlert, DialogClickEventArgs args) { - this.FinishAffinity(); + this.OpenAppSettingsOnPhone(); }); } Dialog dialog = builder.Create(); @@ -685,6 +815,15 @@ namespace StardewModdingAPI } } + private void OpenAppSettingsOnPhone() + { + Intent intent = new Intent(); + intent.SetAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + Android.Net.Uri data = Android.Net.Uri.FromParts("package", this.PackageName, null); + intent.SetData(data); + this.StartActivity(intent); + } + private void CheckUsingServerManagedPolicy() { //("MainActivity.CheckUsingServerManagedPolicy"); diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index c678ed1d..8e21b3a2 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -90,7 +90,9 @@ ..\..\..\..\..\Downloads\Stardew-Valley-v1-25_mod\unknown\assemblies\StardewValley.dll - + + True +