summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-11-30 17:12:49 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-11-30 17:12:49 -0500
commit919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9 (patch)
tree5c03ee1bd3cd658586755694940ac329491d6493 /src/SMAPI
parent3ca6fb562417748c87567d6bb6915d56b2c1b57c (diff)
parentd1d09ae1df63826dd453aa0347d668f420754ed7 (diff)
downloadSMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.tar.gz
SMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.tar.bz2
SMAPI-919bbe94aa027c8a4ff8db4bdb50e8e2a28c48d9.zip
Merge branch 'beta' into develop
Diffstat (limited to 'src/SMAPI')
-rw-r--r--src/SMAPI/Constants.cs93
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForMap.cs90
-rw-r--r--src/SMAPI/Framework/Content/ContentCache.cs21
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs41
-rw-r--r--src/SMAPI/Framework/ContentManagers/BaseContentManager.cs17
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs14
-rw-r--r--src/SMAPI/Framework/Input/GamePadStateBuilder.cs19
-rw-r--r--src/SMAPI/Framework/InternalExtensions.cs7
-rw-r--r--src/SMAPI/Framework/Logging/LogManager.cs33
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs5
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs10
-rw-r--r--src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs2
-rw-r--r--src/SMAPI/Framework/SCore.cs33
-rw-r--r--src/SMAPI/Framework/SGame.cs162
-rw-r--r--src/SMAPI/Framework/SModHooks.cs30
-rw-r--r--src/SMAPI/GameFramework.cs7
-rw-r--r--src/SMAPI/IAssetDataForMap.cs3
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs13
-rw-r--r--src/SMAPI/Metadata/InstructionMetadata.cs10
-rw-r--r--src/SMAPI/PatchMapMode.cs15
-rw-r--r--src/SMAPI/Program.cs33
-rw-r--r--src/SMAPI/SMAPI.config.json2
-rw-r--r--src/SMAPI/SMAPI.csproj40
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