diff options
| author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-11-30 17:12:49 -0500 |
|---|---|---|
| committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-11-30 17:12:49 -0500 |
| commit | 919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9 (patch) | |
| tree | 5c03ee1bd3cd658586755694940ac329491d6493 /src/SMAPI | |
| parent | 3ca6fb562417748c87567d6bb6915d56b2c1b57c (diff) | |
| parent | d1d09ae1df63826dd453aa0347d668f420754ed7 (diff) | |
| download | SMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.tar.gz SMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.tar.bz2 SMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.zip | |
Merge branch 'beta' into develop
Diffstat (limited to 'src/SMAPI')
24 files changed, 322 insertions, 380 deletions
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 42c3b21b..3ae8661a 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -40,15 +40,10 @@ namespace StardewModdingAPI internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform()); /// <summary>The game framework running the game.</summary> - internal static GameFramework GameFramework { get; } = -#if SMAPI_FOR_XNA - GameFramework.Xna; -#else - GameFramework.MonoGame; -#endif + internal static GameFramework GameFramework { get; } = GameFramework.MonoGame; /// <summary>The game's assembly name.</summary> - internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows ? "Stardew Valley" : "StardewValley"; + internal static string GameAssemblyName { get; } = "Stardew Valley"; /// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary> internal static int? LogScreenId { get; set; } @@ -70,10 +65,10 @@ namespace StardewModdingAPI public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion); /// <summary>The minimum supported version of Stardew Valley.</summary> - public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.4"); + public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.5"); /// <summary>The maximum supported version of Stardew Valley.</summary> - public static ISemanticVersion MaximumGameVersion { get; } = new GameVersion("1.5.4"); + public static ISemanticVersion MaximumGameVersion { get; } = null; /// <summary>The target game platform.</summary> public static GamePlatform TargetPlatform { get; } = EarlyConstants.Platform; @@ -240,18 +235,13 @@ namespace StardewModdingAPI // The game assembly can have one of three names depending how the mod was compiled: // - 'StardewValley': assembly name on Linux/macOS; // - 'Stardew Valley': assembly name on Windows; - // - 'Netcode': an assembly that's separate on Windows only. - resolver.AddWithExplicitNames(AssemblyDefinition.ReadAssembly(typeof(Game1).Assembly.Location), "StardewValley", "Stardew Valley" -#if !SMAPI_FOR_WINDOWS - , "Netcode" -#endif - ); + // - 'Netcode': an assembly that was separate on Windows only before Stardew Valley 1.5.5. + resolver.AddWithExplicitNames(AssemblyDefinition.ReadAssembly(typeof(Game1).Assembly.Location), "StardewValley", "Stardew Valley", "Netcode"); } /// <summary>Get metadata for mapping assemblies to the current platform.</summary> /// <param name="targetPlatform">The target game platform.</param> - /// <param name="framework">The game framework running the game.</param> - internal static PlatformAssemblyMap GetAssemblyMap(Platform targetPlatform, GameFramework framework) + internal static PlatformAssemblyMap GetAssemblyMap(Platform targetPlatform) { var removeAssemblyReferences = new List<string>(); var targetAssemblies = new List<Assembly>(); @@ -260,61 +250,26 @@ namespace StardewModdingAPI removeAssemblyReferences.Add("StardewModdingAPI.Toolkit.CoreInterfaces"); targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly); - // get changes for platform - if (Constants.Platform != Platform.Windows) - { - removeAssemblyReferences.AddRange(new[] - { - "Netcode", - "Stardew Valley" - }); - targetAssemblies.Add( - typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/macOS - ); - } - else + // XNA Framework before Stardew Valley 1.5.5 + removeAssemblyReferences.AddRange(new[] { - removeAssemblyReferences.Add( - "StardewValley" - ); - targetAssemblies.AddRange(new[] - { - typeof(Netcode.NetBool).Assembly, - typeof(StardewValley.Game1).Assembly - }); - } + "Microsoft.Xna.Framework", + "Microsoft.Xna.Framework.Game", + "Microsoft.Xna.Framework.Graphics", + "Microsoft.Xna.Framework.Xact" + }); + targetAssemblies.Add( + typeof(Microsoft.Xna.Framework.Vector2).Assembly + ); - // get changes for game framework - switch (framework) - { - case GameFramework.MonoGame: - removeAssemblyReferences.AddRange(new[] - { - "Microsoft.Xna.Framework", - "Microsoft.Xna.Framework.Game", - "Microsoft.Xna.Framework.Graphics", - "Microsoft.Xna.Framework.Xact" - }); - targetAssemblies.Add( - typeof(Microsoft.Xna.Framework.Vector2).Assembly - ); - break; - - case GameFramework.Xna: - removeAssemblyReferences.Add( - "MonoGame.Framework" - ); - targetAssemblies.AddRange(new[] - { - typeof(Microsoft.Xna.Framework.Vector2).Assembly, - typeof(Microsoft.Xna.Framework.Game).Assembly, - typeof(Microsoft.Xna.Framework.Graphics.SpriteBatch).Assembly - }); - break; + // `Netcode.dll` merged into the game assembly in Stardew Valley 1.5.5 + removeAssemblyReferences.Add( + "Netcode" + ); - default: - throw new InvalidOperationException($"Unknown game framework '{framework}'."); - } + // Stardew Valley reference + removeAssemblyReferences.Add("StardewValley"); + targetAssemblies.Add(typeof(StardewValley.Game1).Assembly); return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray()); } diff --git a/src/SMAPI/Framework/Content/AssetDataForMap.cs b/src/SMAPI/Framework/Content/AssetDataForMap.cs index 4f810948..0a5fa7e7 100644 --- a/src/SMAPI/Framework/Content/AssetDataForMap.cs +++ b/src/SMAPI/Framework/Content/AssetDataForMap.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using StardewModdingAPI.Toolkit.Utilities; +using StardewValley; using xTile; using xTile.Layers; using xTile.Tiles; @@ -25,18 +26,17 @@ namespace StardewModdingAPI.Framework.Content : base(locale, assetName, data, getNormalizedPath, onDataReplaced) { } /// <inheritdoc /> - /// <remarks>Derived from <see cref="StardewValley.GameLocation.ApplyMapOverride"/> with a few changes: + /// <remarks>Derived from <see cref="GameLocation.ApplyMapOverride(Map,string,Rectangle?,Rectangle?)"/> with a few changes: /// - can be applied directly to the maps when loading, before the location is created; - /// - added support for source/target areas; + /// - added support for patch modes (overlay, replace by layer, or fully replace); /// - added disambiguation if source has a modified version of the same tilesheet, instead of copying tiles into the target tilesheet; - /// - changed to always overwrite tiles within the target area (to avoid edge cases where some tiles are only partly applied); /// - fixed copying tilesheets (avoid "The specified TileSheet was not created for use with this map" error); /// - fixed tilesheets not added at the end (via z_ prefix), which can cause crashes in game code which depends on hardcoded tilesheet indexes; /// - fixed issue where different tilesheets are linked by ID. /// </remarks> - public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null) + public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMapMode patchMode = PatchMapMode.Overlay) { - var target = this.Data; + Map target = this.Data; // get areas { @@ -84,10 +84,13 @@ namespace StardewModdingAPI.Framework.Content tilesheetMap[sourceSheet] = targetSheet; } - // get layer map - IDictionary<Layer, Layer> layerMap = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id)); + // get target layers + IDictionary<Layer, Layer> sourceToTargetLayers = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id)); + HashSet<Layer> orphanedTargetLayers = new HashSet<Layer>(target.Layers.Except(sourceToTargetLayers.Values)); // apply tiles + bool replaceAll = patchMode == PatchMapMode.Replace; + bool replaceByLayer = patchMode == PatchMapMode.ReplaceByLayer; for (int x = 0; x < sourceArea.Value.Width; x++) { for (int y = 0; y < sourceArea.Value.Height; y++) @@ -96,47 +99,37 @@ namespace StardewModdingAPI.Framework.Content Point sourcePos = new Point(sourceArea.Value.X + x, sourceArea.Value.Y + y); Point targetPos = new Point(targetArea.Value.X + x, targetArea.Value.Y + y); + // replace tiles on target-only layers + if (replaceAll) + { + foreach (Layer targetLayer in orphanedTargetLayers) + targetLayer.Tiles[targetPos.X, targetPos.Y] = null; + } + // merge layers foreach (Layer sourceLayer in source.Layers) { // get layer - Layer targetLayer = layerMap[sourceLayer]; + Layer targetLayer = sourceToTargetLayers[sourceLayer]; if (targetLayer == null) { target.AddLayer(targetLayer = new Layer(sourceLayer.Id, target, target.Layers[0].LayerSize, Layer.m_tileSize)); - layerMap[sourceLayer] = target.GetLayer(sourceLayer.Id); + sourceToTargetLayers[sourceLayer] = target.GetLayer(sourceLayer.Id); } // copy layer properties targetLayer.Properties.CopyFrom(sourceLayer.Properties); - // copy tiles + // create new tile Tile sourceTile = sourceLayer.Tiles[sourcePos.X, sourcePos.Y]; - Tile targetTile; - switch (sourceTile) - { - case StaticTile _: - targetTile = new StaticTile(targetLayer, tilesheetMap[sourceTile.TileSheet], sourceTile.BlendMode, sourceTile.TileIndex); - break; - - case AnimatedTile animatedTile: - { - StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length]; - for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame) - { - StaticTile frameTile = animatedTile.TileFrames[frame]; - tileFrames[frame] = new StaticTile(targetLayer, tilesheetMap[frameTile.TileSheet], frameTile.BlendMode, frameTile.TileIndex); - } - targetTile = new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval); - } - break; - - default: // null or unhandled type - targetTile = null; - break; - } - targetTile?.Properties.CopyFrom(sourceTile.Properties); - targetLayer.Tiles[targetPos.X, targetPos.Y] = targetTile; + Tile newTile = sourceTile != null + ? this.CreateTile(sourceTile, targetLayer, tilesheetMap[sourceTile.TileSheet]) + : null; + newTile?.Properties.CopyFrom(sourceTile.Properties); + + // replace tile + if (newTile != null || replaceByLayer || replaceAll) + targetLayer.Tiles[targetPos.X, targetPos.Y] = newTile; } } } @@ -146,6 +139,33 @@ namespace StardewModdingAPI.Framework.Content /********* ** Private methods *********/ + /// <summary>Create a new tile for the target map.</summary> + /// <param name="sourceTile">The source tile to copy.</param> + /// <param name="targetLayer">The target layer.</param> + /// <param name="targetSheet">The target tilesheet.</param> + private Tile CreateTile(Tile sourceTile, Layer targetLayer, TileSheet targetSheet) + { + switch (sourceTile) + { + case StaticTile _: + return new StaticTile(targetLayer, targetSheet, sourceTile.BlendMode, sourceTile.TileIndex); + + case AnimatedTile animatedTile: + { + StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length]; + for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame) + { + StaticTile frameTile = animatedTile.TileFrames[frame]; + tileFrames[frame] = new StaticTile(targetLayer, targetSheet, frameTile.BlendMode, frameTile.TileIndex); + } + + return new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval); + } + + default: // null or unhandled type + return null; + } + } /// <summary>Normalize a map tilesheet path for comparison. This value should *not* be used as the actual tilesheet path.</summary> /// <param name="path">The path to normalize.</param> private string NormalizeTilesheetPathForComparison(string path) diff --git a/src/SMAPI/Framework/Content/ContentCache.cs b/src/SMAPI/Framework/Content/ContentCache.cs index 7edc9ab9..8e0c6228 100644 --- a/src/SMAPI/Framework/Content/ContentCache.cs +++ b/src/SMAPI/Framework/Content/ContentCache.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; -using Microsoft.Xna.Framework; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -18,9 +17,6 @@ namespace StardewModdingAPI.Framework.Content /// <summary>The underlying asset cache.</summary> private readonly IDictionary<string, object> Cache; - /// <summary>Applies platform-specific asset key normalization so it's consistent with the underlying cache.</summary> - private readonly Func<string, string> NormalizeAssetNameForPlatform; - /********* ** Accessors @@ -48,17 +44,7 @@ namespace StardewModdingAPI.Framework.Content /// <param name="reflection">Simplifies access to private game code.</param> public ContentCache(LocalizedContentManager contentManager, Reflector reflection) { - // init this.Cache = reflection.GetField<Dictionary<string, object>>(contentManager, "loadedAssets").GetValue(); - - // get key normalization logic - if (Constants.GameFramework == GameFramework.Xna) - { - IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath"); - this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path); - } - else - this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic } /**** @@ -75,23 +61,24 @@ namespace StardewModdingAPI.Framework.Content /**** ** Normalize ****/ - /// <summary>Normalize path separators in a file path. For asset keys, see <see cref="NormalizeKey"/> instead.</summary> + /// <summary>Normalize path separators in an asset name.</summary> /// <param name="path">The file path to normalize.</param> [Pure] public string NormalizePathSeparators(string path) { - return PathUtilities.NormalizePath(path); + return PathUtilities.NormalizeAssetName(path); } /// <summary>Normalize a cache key so it's consistent with the underlying cache.</summary> /// <param name="key">The asset key.</param> + /// <remarks>This is equivalent to <see cref="NormalizePathSeparators"/> with added file extension logic.</remarks> [Pure] public string NormalizeKey(string key) { key = this.NormalizePathSeparators(key); return key.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase) ? key.Substring(0, key.Length - 4) - : this.NormalizeAssetNameForPlatform(key); + : key; } /**** diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index d0e759c2..b6f1669a 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -64,6 +64,9 @@ namespace StardewModdingAPI.Framework /// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary> private readonly LocalizedContentManager VanillaContentManager; + /// <summary>The language enum values indexed by locale code.</summary> + private Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>> LocaleCodes; + /********* ** Accessors @@ -133,6 +136,7 @@ namespace StardewModdingAPI.Framework this.ContentManagers.Add(contentManagerForAssetPropagation); this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory); this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations); + this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes); } /// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary> @@ -195,6 +199,10 @@ namespace StardewModdingAPI.Framework /// <summary>Perform any cleanup needed when the locale changes.</summary> public void OnLocaleChanged() { + // rebuild locale cache (which may change due to custom mod languages) + this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes); + + // reload affected content this.ContentManagerLock.InReadLock(() => { foreach (IContentManager contentManager in this.ContentManagers) @@ -408,6 +416,25 @@ namespace StardewModdingAPI.Framework return tilesheets ?? new TilesheetReference[0]; } + /// <summary>Get the language enum which corresponds to a locale code (e.g. <see cref="LocalizedContentManager.LanguageCode.fr"/> given <c>fr-FR</c>).</summary> + /// <param name="locale">The locale code to search. This must exactly match the language; no fallback is performed.</param> + /// <param name="language">The matched language enum, if any.</param> + /// <returns>Returns whether a valid language was found.</returns> + public bool TryGetLanguageEnum(string locale, out LocalizedContentManager.LanguageCode language) + { + return this.LocaleCodes.Value.TryGetValue(locale, out language); + } + + /// <summary>Get the locale code which corresponds to a language enum (e.g. <c>fr-FR</c> given <see cref="LocalizedContentManager.LanguageCode.fr"/>).</summary> + /// <param name="language">The language enum to search.</param> + public string GetLocaleCode(LocalizedContentManager.LanguageCode language) + { + if (language == LocalizedContentManager.LanguageCode.mod && LocalizedContentManager.CurrentModLanguage == null) + return null; + + return Game1.content.LanguageCodeString(language); + } + /// <summary>Dispose held resources.</summary> public void Dispose() { @@ -457,5 +484,19 @@ namespace StardewModdingAPI.Framework return false; } } + + /// <summary>Get the language enums (like <see cref="LocalizedContentManager.LanguageCode.ja"/>) indexed by locale code (like <c>ja-JP</c>).</summary> + private IDictionary<string, LocalizedContentManager.LanguageCode> GetLocaleCodes() + { + IDictionary<string, LocalizedContentManager.LanguageCode> map = new Dictionary<string, LocalizedContentManager.LanguageCode>(); + foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode))) + { + string locale = this.GetLocaleCode(code); + if (locale != null) + map[locale] = code; + } + + return map; + } } } diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs index 7244a534..5645c0fa 100644 --- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs @@ -38,9 +38,6 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <summary>A callback to invoke when the content manager is being disposed.</summary> private readonly Action<BaseContentManager> OnDisposing; - /// <summary>The language enum values indexed by locale code.</summary> - protected IDictionary<string, LanguageCode> LanguageCodes { get; } - /// <summary>A list of disposable assets.</summary> private readonly List<WeakReference<IDisposable>> Disposables = new List<WeakReference<IDisposable>>(); @@ -92,7 +89,6 @@ namespace StardewModdingAPI.Framework.ContentManagers this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; // get asset data - this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase); this.BaseDisposableReferences = reflection.GetField<List<IDisposable>>(this, "disposableAssets").GetValue(); } @@ -292,7 +288,7 @@ namespace StardewModdingAPI.Framework.ContentManagers if (lastSepIndex >= 0) { string suffix = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); - if (this.LanguageCodes.ContainsKey(suffix)) + if (this.Coordinator.TryGetLanguageEnum(suffix, out _)) { assetName = cacheKey.Substring(0, lastSepIndex); localeCode = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); @@ -311,17 +307,6 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <param name="language">The language to check.</param> protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language); - /// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary> - private IDictionary<LanguageCode, string> GetKeyLocales() - { - // create locale => code map - IDictionary<LanguageCode, string> map = new Dictionary<LanguageCode, string>(); - foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) - map[code] = this.GetLocale(code); - - return map; - } - /// <summary>Get the asset name from a cache key.</summary> /// <param name="cacheKey">The input cache key.</param> private string GetAssetName(string cacheKey) diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 38bcf153..7a49dd36 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -249,7 +249,7 @@ namespace StardewModdingAPI.Framework.ContentManagers // extract language code int splitIndex = rawAsset.LastIndexOf('.'); - if (splitIndex != -1 && this.LanguageCodes.TryGetValue(rawAsset.Substring(splitIndex + 1), out language)) + if (splitIndex != -1 && this.Coordinator.TryGetLanguageEnum(rawAsset.Substring(splitIndex + 1), out language)) { assetName = rawAsset.Substring(0, splitIndex); return true; diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs index d24ffb81..beb90a5d 100644 --- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs @@ -2,12 +2,12 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using BmFont; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Reflection; -using StardewModdingAPI.Internal; using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -130,6 +130,14 @@ namespace StardewModdingAPI.Framework.ContentManagers } break; + // unpacked Bitmap font + case ".fnt": + { + string source = File.ReadAllText(file.FullName); + asset = (T)(object)new XmlSource(source); + } + break; + // unpacked data case ".json": { @@ -172,13 +180,11 @@ namespace StardewModdingAPI.Framework.ContentManagers break; default: - throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.json', '.png', '.tbin', or '.xnb'."); + throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.fnt', '.json', '.png', '.tbin', or '.xnb'."); } } catch (Exception ex) when (!(ex is SContentLoadException)) { - if (ex.GetInnermostException() is DllNotFoundException dllEx && dllEx.Message == "libgdiplus.dylib") - throw GetContentError("couldn't find libgdiplus, which is needed to load mod images. Make sure Mono is installed and you're running the game through the normal launcher."); throw new SContentLoadException($"The content manager failed loading content asset '{assetName}' from {this.Name}.", ex); } diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs index f5f2d916..b0bb7f80 100644 --- a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs +++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; @@ -157,11 +158,8 @@ namespace StardewModdingAPI.Framework.Input yield break; // buttons - foreach (var pair in this.ButtonStates) - { - if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) - yield return button.ToSButton(); - } + foreach (Buttons button in this.GetPressedGamePadButtons()) + yield return button.ToSButton(); // triggers if (this.LeftTrigger > 0.2f) @@ -201,7 +199,7 @@ namespace StardewModdingAPI.Framework.Input rightThumbStick: this.RightStickPos, leftTrigger: this.LeftTrigger, rightTrigger: this.RightTrigger, - buttons: this.GetButtonBitmask() // MonoGame requires one bitmask here; don't specify multiple values + buttons: this.GetPressedGamePadButtons().ToArray() ); return this.State.Value; @@ -211,17 +209,14 @@ namespace StardewModdingAPI.Framework.Input /********* ** Private methods *********/ - /// <summary>Get a bitmask representing the pressed buttons.</summary> - private Buttons GetButtonBitmask() + /// <summary>Get the pressed gamepad buttons.</summary> + private IEnumerable<Buttons> GetPressedGamePadButtons() { - Buttons flag = 0; foreach (var pair in this.ButtonStates) { if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) - flag |= button; + yield return button; } - - return flag; } } } diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs index 6c9a5f3b..4cb77a45 100644 --- a/src/SMAPI/Framework/InternalExtensions.cs +++ b/src/SMAPI/Framework/InternalExtensions.cs @@ -6,7 +6,6 @@ using System.Threading; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Reflection; -using StardewValley; using StardewValley.Menus; namespace StardewModdingAPI.Framework @@ -150,11 +149,7 @@ namespace StardewModdingAPI.Framework /// <param name="reflection">The reflection helper with which to access private fields.</param> public static bool IsOpen(this SpriteBatch spriteBatch, Reflector reflection) { - string fieldName = Constants.GameFramework == GameFramework.Xna - ? "inBeginEndPair" - : "_beginCalled"; - - return reflection.GetFi |
