diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 91e55c1c..b2640d77 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -20,7 +20,7 @@ namespace StardewModdingAPI ** Public ****/ /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.4.1.2", allowNonStandard: true); + public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.4.1.5", allowNonStandard: true); /// The minimum supported version of Stardew Valley. public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5"); @@ -109,6 +109,8 @@ namespace StardewModdingAPI /// The language code for non-translated mod assets. internal static LocalizedContentManager.LanguageCode DefaultLanguage { get; } = LocalizedContentManager.LanguageCode.en; + internal static bool MonoModInit { get; set; } = true; + /********* ** Internal methods diff --git a/src/SMAPI/Framework/ModLoading/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/RewriteHelper.cs index 795d0d2c..75160088 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteHelper.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteHelper.cs @@ -91,6 +91,12 @@ namespace StardewModdingAPI.Framework.ModLoading if (definition.Name != reference.Name) return false; + // same return type + if(definition is MethodInfo methodDefinition) + { + if(!RewriteHelper.IsSameType(methodDefinition.ReturnType, reference.ReturnType)) + return false; + } // same arguments ParameterInfo[] definitionParameters = definition.GetParameters(); ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); diff --git a/src/SMAPI/Framework/Patching/GamePatcher.cs b/src/SMAPI/Framework/Patching/GamePatcher.cs index 56d6a88d..66978eb3 100644 --- a/src/SMAPI/Framework/Patching/GamePatcher.cs +++ b/src/SMAPI/Framework/Patching/GamePatcher.cs @@ -29,11 +29,12 @@ namespace StardewModdingAPI.Framework.Patching /// The patches to apply. public void Apply(params IHarmonyPatch[] patches) { - if (Build.VERSION.SdkInt < BuildVersionCodes.M) - return; - if (!HarmonyDetourBridge.Initialized) + if (!HarmonyDetourBridge.Initialized && Constants.MonoModInit) { - HarmonyDetourBridge.Init(); + try { + HarmonyDetourBridge.Init(); + } + catch { Constants.MonoModInit = false; } } HarmonyInstance harmony = HarmonyInstance.Create("io.smapi"); @@ -41,19 +42,16 @@ namespace StardewModdingAPI.Framework.Patching { try { - patch.Apply(harmony); + if(Constants.MonoModInit) + patch.Apply(harmony); } catch (Exception ex) { + Constants.MonoModInit = false; 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); } } - - //Keeping for reference - //if (Build.VERSION.SdkInt > BuildVersionCodes.LollipopMr1) - //{ - //} } } } diff --git a/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs b/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs index e83738ca..b77133bd 100644 --- a/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs +++ b/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewValley; using StardewValley.Menus; @@ -106,5 +107,14 @@ namespace StardewModdingAPI.Framework.RewriteFacades } } } + public static new void createItemDebris(Item item, Vector2 origin, int direction, GameLocation location = null, int groundLevel = -1) + { + Game1.createItemDebris(item, origin, direction, location, groundLevel); + } + + public static void changeMusicTrack(string newTrackName) + { + Game1.changeMusicTrack(newTrackName, false, MusicContext.Default); + } } } diff --git a/src/SMAPI/Framework/RewriteFacades/GameLocationMethods.cs b/src/SMAPI/Framework/RewriteFacades/GameLocationMethods.cs index d9114ae4..a48d7a96 100644 --- a/src/SMAPI/Framework/RewriteFacades/GameLocationMethods.cs +++ b/src/SMAPI/Framework/RewriteFacades/GameLocationMethods.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Netcode; using StardewValley; namespace StardewModdingAPI.Framework.RewriteFacades { public class GameLocationMethods : GameLocation { + public void playSound(string audioName) + { + base.playSound(audioName, StardewValley.Network.NetAudio.SoundContext.Default); + } } } diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 221d251f..58bd000b 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -748,7 +748,7 @@ namespace StardewModdingAPI.Framework shopMenu.onPurchase, shopMenu.onSell, shopMenu.storeContext); } } - } + } /********* ** World & player events @@ -977,7 +977,9 @@ namespace StardewModdingAPI.Framework this.ExitGameImmediately("The game crashed when drawing, and SMAPI was unable to recover the game."); return; } - + } + finally + { // recover sprite batch try { diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index e529f5f5..a05a0dd1 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -89,6 +89,7 @@ namespace StardewModdingAPI.Metadata yield return new MethodParentRewriter(typeof(Utility), typeof(UtilityMethods)); yield return new MethodParentRewriter(typeof(DayTimeMoneyBox), typeof(DayTimeMoneyBoxMethods)); yield return new MethodParentRewriter(typeof(SaveGame), typeof(SaveGameMethods)); + yield return new MethodParentRewriter(typeof(GameLocation), typeof(GameLocationMethods)); //Constructor Rewrites yield return new MethodParentRewriter(typeof(HUDMessage), typeof(HUDMessageMethods)); diff --git a/src/SMAPI/SGameConsole.cs b/src/SMAPI/SGameConsole.cs index 9e0fdc95..115662a2 100644 --- a/src/SMAPI/SGameConsole.cs +++ b/src/SMAPI/SGameConsole.cs @@ -17,7 +17,9 @@ namespace StardewModdingAPI public bool isVisible; private readonly LinkedList> consoleMessageQueue = new LinkedList>(); + private Dictionary> parseTextCache = new Dictionary>(); private MobileScrollbox scrollbox; + private MobileScrollbar scrollbar; private ClickableTextureComponent commandButton; @@ -29,7 +31,8 @@ namespace StardewModdingAPI private int scrollLastY = 0; - private int MaxScrollBoxHeight => (int)(Game1.graphics.PreferredBackBufferHeight * 20 / Game1.NativeZoomLevel); + private int MaxScrollBoxHeight => (int)(Game1.graphics.PreferredBackBufferHeight * 100 / Game1.NativeZoomLevel); + private int ScrollBoxHeight => (int)(Game1.graphics.PreferredBackBufferHeight / Game1.NativeZoomLevel); private int MaxTextAreaWidth => (int)((Game1.graphics.PreferredBackBufferWidth - 32) / Game1.NativeZoomLevel); @@ -41,9 +44,13 @@ namespace StardewModdingAPI internal void InitializeContent(LocalizedContentManager content) { - this.scrollbox = new MobileScrollbox(0, 0, this.MaxTextAreaWidth, (int)(Game1.graphics.PreferredBackBufferHeight / Game1.NativeZoomLevel), this.MaxScrollBoxHeight, - new Rectangle(0, 0, (int)(Game1.graphics.PreferredBackBufferWidth / Game1.NativeZoomLevel), (int)(Game1.graphics.PreferredBackBufferHeight / Game1.NativeZoomLevel))); this.smallFont = content.Load(@"Fonts\SmallFont"); + Game1.mobileSpriteSheet = content.Load(@"LooseSprites\\MobileAtlas_manually_made"); + this.scrollbar = new MobileScrollbar(0, 96, 16, this.ScrollBoxHeight - 192); + this.scrollbox = new MobileScrollbox(0, 0, this.MaxTextAreaWidth, this.ScrollBoxHeight, this.MaxScrollBoxHeight, + new Rectangle(0, 0, (int)(Game1.graphics.PreferredBackBufferWidth / Game1.NativeZoomLevel), this.ScrollBoxHeight), + this.scrollbar + ); } public void Show() @@ -58,6 +65,12 @@ namespace StardewModdingAPI public override void receiveLeftClick(int x, int y, bool playSound = true) { + if (this.scrollbar.sliderContains(x, y) || this.scrollbar.sliderRunnerContains(x, y)) + { + float num = this.scrollbar.setY(y); + this.scrollbox.setYOffsetForScroll(-((int)((num * this.scrollbox.getMaxYOffset()) / 100f))); + Game1.playSound("shwip"); + } if (this.upperRightCloseButton.bounds.Contains(x, y)) { @@ -126,6 +139,7 @@ namespace StardewModdingAPI this.consoleMessageQueue.AddFirst(new KeyValuePair(level, consoleMessage)); if (this.consoleMessageQueue.Count > 2000) { + this.parseTextCache.Remove(this.consoleMessageQueue.Last.Value.Value); this.consoleMessageQueue.RemoveLast(); } } @@ -136,27 +150,39 @@ namespace StardewModdingAPI this.scrollbox.update(time); } - private string _parseText(string text) + private LinkedList _parseText(string text) { + if (this.parseTextCache.TryGetValue(text, out LinkedList returnString)) + { + return returnString; + } + returnString = new LinkedList(); string line = string.Empty; - string returnString = string.Empty; string[] strings = text.Split("\n"); foreach (string t in strings) { + if (this.smallFont.MeasureString(t).X < this.MaxTextAreaWidth) + { + returnString.AddFirst(t); + continue; + } string[] wordArray = t.Split(' '); + Vector2 masureResult = new Vector2(0,0); foreach (string word in wordArray) { - if (this.smallFont.MeasureString(line + word).X > this.MaxTextAreaWidth) + masureResult += this.smallFont.MeasureString(word + " "); + if (masureResult.X > this.MaxTextAreaWidth) { - returnString = returnString + line + '\n'; + returnString.AddFirst(line); line = string.Empty; + masureResult = new Vector2(0, 0); } line = line + word + ' '; } - returnString = returnString + line + '\n'; + returnString.AddFirst(line); line = string.Empty; } - returnString.TrimEnd('\n'); + this.parseTextCache.TryAdd(text, returnString); return returnString; } @@ -185,39 +211,52 @@ namespace StardewModdingAPI public override void draw(SpriteBatch b) { + this.scrollbar.draw(b); this.scrollbox.setUpForScrollBoxDrawing(b); lock (this.consoleMessageQueue) { float offset = 0; + Vector2 size = this.smallFont.MeasureString("Aa"); foreach (var log in this.consoleMessageQueue) { - string text = this._parseText(log.Value); - Vector2 size = this.smallFont.MeasureString(text); - float y = Game1.game1.screen.Height - size.Y - offset - this.scrollbox.getYOffsetForScroll(); - offset += size.Y; - switch (log.Key) + LinkedList textArray = this._parseText(log.Value); + float baseOffset = Game1.game1.screen.Height - this.scrollbox.getYOffsetForScroll(); + if (baseOffset - size.Y * textArray.Count - offset > this.ScrollBoxHeight) { - case ConsoleLogLevel.Critical: - case ConsoleLogLevel.Error: - b.DrawString(this.smallFont, text, new Vector2(16, y), Color.Red); - break; - case ConsoleLogLevel.Alert: - case ConsoleLogLevel.Warn: - b.DrawString(this.smallFont, text, new Vector2(16, y), Color.Orange); - break; - case ConsoleLogLevel.Info: - case ConsoleLogLevel.Success: - b.DrawString(this.smallFont, text, new Vector2(16, y), Color.AntiqueWhite); - break; - case ConsoleLogLevel.Debug: - case ConsoleLogLevel.Trace: - b.DrawString(this.smallFont, text, new Vector2(16, y), Color.LightGray); - break; - default: - b.DrawString(this.smallFont, text, new Vector2(16, y), Color.LightGray); - break; + offset += size.Y * textArray.Count; + } + else + { + foreach (string text in textArray) + { + float y = baseOffset - size.Y - offset; + if (y < -16) + continue; + offset += size.Y; + switch (log.Key) + { + case ConsoleLogLevel.Critical: + case ConsoleLogLevel.Error: + b.DrawString(this.smallFont, text, new Vector2(16, y), Color.Red); + break; + case ConsoleLogLevel.Alert: + case ConsoleLogLevel.Warn: + b.DrawString(this.smallFont, text, new Vector2(16, y), Color.Orange); + break; + case ConsoleLogLevel.Info: + case ConsoleLogLevel.Success: + b.DrawString(this.smallFont, text, new Vector2(16, y), Color.AntiqueWhite); + break; + case ConsoleLogLevel.Debug: + case ConsoleLogLevel.Trace: + b.DrawString(this.smallFont, text, new Vector2(16, y), Color.LightGray); + break; + default: + b.DrawString(this.smallFont, text, new Vector2(16, y), Color.LightGray); + break; + } + } } - if (offset > this.MaxScrollBoxHeight) { break; diff --git a/src/SMAPI/SMainActivity.cs b/src/SMAPI/SMainActivity.cs index 321090df..127dbd72 100644 --- a/src/SMAPI/SMainActivity.cs +++ b/src/SMAPI/SMainActivity.cs @@ -21,6 +21,7 @@ using File = Java.IO.File; using Microsoft.AppCenter; using Newtonsoft.Json; using Microsoft.AppCenter.Crashes; +using Android.Content; namespace StardewModdingAPI { @@ -178,7 +179,11 @@ namespace StardewModdingAPI public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { - base.OnRequestPermissionsResult(requestCode, permissions, grantResults); + try + { + base.OnRequestPermissionsResult(requestCode, permissions, grantResults); + } + catch (ActivityNotFoundException) { } if (this.HasPermissions) this.OnCreatePartTwo(); }