Merge branch 'stable376' into develop
# Conflicts: # src/SMAPI.Tests/SMAPI.Tests.csproj # src/SMAPI.Toolkit/SMAPI.Toolkit.csproj # src/SMAPI/Constants.cs # src/SMAPI/Framework/Logging/LogManager.cs # src/SMAPI/Framework/SCore.cs
This commit is contained in:
commit
e1ad4d7b9c
|
@ -68,7 +68,7 @@ csharp_style_expression_bodied_accessors = true:suggestion
|
|||
csharp_style_inlined_variable_declaration = true:warning
|
||||
|
||||
# avoid superfluous braces
|
||||
csharp_prefer_braces = false:suggestion
|
||||
csharp_prefer_braces = false:hint
|
||||
|
||||
##########
|
||||
## Column guidelines
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<!--set properties -->
|
||||
<PropertyGroup>
|
||||
<Version>3.7.5</Version>
|
||||
<Version>3.7.6</Version>
|
||||
<Product>SMAPI</Product>
|
||||
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -49,8 +49,8 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
||||
<AndroidUseAapt2>false</AndroidUseAapt2>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
|
||||
<AndroidDexTool>dx</AndroidDexTool>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -12,9 +12,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData
|
|||
/// <summary>The item type.</summary>
|
||||
public ItemType Type { get; }
|
||||
|
||||
/// <summary>The item instance.</summary>
|
||||
/// <summary>A sample item instance.</summary>
|
||||
public Item Item { get; }
|
||||
|
||||
/// <summary>Create an item instance.</summary>
|
||||
public Func<Item> CreateItem { get; }
|
||||
|
||||
/// <summary>The item's unique ID for its type.</summary>
|
||||
public int ID { get; }
|
||||
|
||||
|
@ -31,12 +34,23 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData
|
|||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="type">The item type.</param>
|
||||
/// <param name="id">The unique ID (if different from the item's parent sheet index).</param>
|
||||
/// <param name="item">The item instance.</param>
|
||||
public SearchableItem(ItemType type, int id, Item item)
|
||||
/// <param name="createItem">Create an item instance.</param>
|
||||
public SearchableItem(ItemType type, int id, Func<SearchableItem, Item> createItem)
|
||||
{
|
||||
this.Type = type;
|
||||
this.ID = id;
|
||||
this.Item = item;
|
||||
this.CreateItem = () => createItem(this);
|
||||
this.Item = createItem(this);
|
||||
}
|
||||
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="item">The item metadata to copy.</param>
|
||||
public SearchableItem(SearchableItem item)
|
||||
{
|
||||
this.Type = item.Type;
|
||||
this.ID = item.ID;
|
||||
this.CreateItem = item.CreateItem;
|
||||
this.Item = item.Item;
|
||||
}
|
||||
|
||||
/// <summary>Get whether the item name contains a case-insensitive substring.</summary>
|
||||
|
|
|
@ -6,6 +6,7 @@ using Microsoft.Xna.Framework;
|
|||
using Microsoft.Xna.Framework.Content;
|
||||
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
|
||||
using StardewValley;
|
||||
using StardewValley.GameData.FishPond;
|
||||
using StardewValley.Menus;
|
||||
using StardewValley.Objects;
|
||||
using StardewValley.Tools;
|
||||
|
@ -30,47 +31,77 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
[SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "TryCreate invokes the lambda immediately.")]
|
||||
public IEnumerable<SearchableItem> GetAll()
|
||||
{
|
||||
//
|
||||
//
|
||||
// Be careful about closure variable capture here!
|
||||
//
|
||||
// SearchableItem stores the Func<Item> to create new instances later. Loop variables passed into the
|
||||
// function will be captured, so every func in the loop will use the value from the last iteration. Use the
|
||||
// TryCreate(type, id, entity => item) form to avoid the issue, or create a local variable to pass in.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
IEnumerable<SearchableItem> GetAllRaw()
|
||||
{
|
||||
// get tools
|
||||
for (int quality = Tool.stone; quality <= Tool.iridium; quality++)
|
||||
for (int q = Tool.stone; q <= Tool.iridium; q++)
|
||||
{
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.axe, () => ToolFactory.getToolFromDescription(ToolFactory.axe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.hoe, () => ToolFactory.getToolFromDescription(ToolFactory.hoe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.pickAxe, () => ToolFactory.getToolFromDescription(ToolFactory.pickAxe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.wateringCan, () => ToolFactory.getToolFromDescription(ToolFactory.wateringCan, quality));
|
||||
int quality = q;
|
||||
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.axe, _ => ToolFactory.getToolFromDescription(ToolFactory.axe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.hoe, _ => ToolFactory.getToolFromDescription(ToolFactory.hoe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.pickAxe, _ => ToolFactory.getToolFromDescription(ToolFactory.pickAxe, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.wateringCan, _ => ToolFactory.getToolFromDescription(ToolFactory.wateringCan, quality));
|
||||
if (quality != Tool.iridium)
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.fishingRod, () => ToolFactory.getToolFromDescription(ToolFactory.fishingRod, quality));
|
||||
yield return this.TryCreate(ItemType.Tool, ToolFactory.fishingRod, _ => ToolFactory.getToolFromDescription(ToolFactory.fishingRod, quality));
|
||||
}
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset, () => new MilkPail()); // these don't have any sort of ID, so we'll just assign some arbitrary ones
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 1, () => new Shears());
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 2, () => new Pan());
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 3, () => new Wand());
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset, _ => new MilkPail()); // these don't have any sort of ID, so we'll just assign some arbitrary ones
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 1, _ => new Shears());
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 2, _ => new Pan());
|
||||
yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 3, _ => new Wand());
|
||||
|
||||
// clothing
|
||||
foreach (int id in Game1.clothingInformation.Keys)
|
||||
yield return this.TryCreate(ItemType.Clothing, id, () => new Clothing(id));
|
||||
{
|
||||
// items
|
||||
HashSet<int> clothingIds = new HashSet<int>();
|
||||
foreach (int id in Game1.clothingInformation.Keys)
|
||||
{
|
||||
if (id < 0)
|
||||
continue; // placeholder data for character customization clothing below
|
||||
|
||||
clothingIds.Add(id);
|
||||
yield return this.TryCreate(ItemType.Clothing, id, p => new Clothing(p.ID));
|
||||
}
|
||||
|
||||
// character customization shirts (some shirts in this range have no data, but game has special logic to handle them)
|
||||
for (int id = 1000; id <= 1111; id++)
|
||||
{
|
||||
if (!clothingIds.Contains(id))
|
||||
yield return this.TryCreate(ItemType.Clothing, id, p => new Clothing(p.ID));
|
||||
}
|
||||
}
|
||||
|
||||
// wallpapers
|
||||
for (int id = 0; id < 112; id++)
|
||||
yield return this.TryCreate(ItemType.Wallpaper, id, () => new Wallpaper(id) { Category = SObject.furnitureCategory });
|
||||
yield return this.TryCreate(ItemType.Wallpaper, id, p => new Wallpaper(p.ID) { Category = SObject.furnitureCategory });
|
||||
|
||||
// flooring
|
||||
for (int id = 0; id < 56; id++)
|
||||
yield return this.TryCreate(ItemType.Flooring, id, () => new Wallpaper(id, isFloor: true) { Category = SObject.furnitureCategory });
|
||||
yield return this.TryCreate(ItemType.Flooring, id, p => new Wallpaper(p.ID, isFloor: true) { Category = SObject.furnitureCategory });
|
||||
|
||||
// equipment
|
||||
foreach (int id in this.TryLoad<int, string>("Data\\Boots").Keys)
|
||||
yield return this.TryCreate(ItemType.Boots, id, () => new Boots(id));
|
||||
yield return this.TryCreate(ItemType.Boots, id, p => new Boots(p.ID));
|
||||
foreach (int id in this.TryLoad<int, string>("Data\\hats").Keys)
|
||||
yield return this.TryCreate(ItemType.Hat, id, () => new Hat(id));
|
||||
yield return this.TryCreate(ItemType.Hat, id, p => new Hat(p.ID));
|
||||
|
||||
// weapons
|
||||
foreach (int id in this.TryLoad<int, string>("Data\\weapons").Keys)
|
||||
{
|
||||
yield return this.TryCreate(ItemType.Weapon, id, () => (id >= 32 && id <= 34)
|
||||
? (Item)new Slingshot(id)
|
||||
: new MeleeWeapon(id)
|
||||
yield return this.TryCreate(ItemType.Weapon, id, p => (p.ID >= 32 && p.ID <= 34)
|
||||
? (Item)new Slingshot(p.ID)
|
||||
: new MeleeWeapon(p.ID)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -78,14 +109,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
foreach (int id in this.TryLoad<int, string>("Data\\Furniture").Keys)
|
||||
{
|
||||
if (id == 1466 || id == 1468 || id == 1680)
|
||||
yield return this.TryCreate(ItemType.Furniture, id, () => new TV(id, Vector2.Zero));
|
||||
yield return this.TryCreate(ItemType.Furniture, id, p => new TV(p.ID, Vector2.Zero));
|
||||
else
|
||||
yield return this.TryCreate(ItemType.Furniture, id, () => new Furniture(id, Vector2.Zero));
|
||||
yield return this.TryCreate(ItemType.Furniture, id, p => new Furniture(p.ID, Vector2.Zero));
|
||||
}
|
||||
|
||||
// craftables
|
||||
foreach (int id in Game1.bigCraftablesInformation.Keys)
|
||||
yield return this.TryCreate(ItemType.BigCraftable, id, () => new SObject(Vector2.Zero, id));
|
||||
yield return this.TryCreate(ItemType.BigCraftable, id, p => new SObject(Vector2.Zero, p.ID));
|
||||
|
||||
// objects
|
||||
foreach (int id in Game1.objectInformation.Keys)
|
||||
|
@ -97,7 +128,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
{
|
||||
foreach (int secretNoteId in this.TryLoad<int, string>("Data\\SecretNotes").Keys)
|
||||
{
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + secretNoteId, () =>
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + secretNoteId, _ =>
|
||||
{
|
||||
SObject note = new SObject(79, 1);
|
||||
note.name = $"{note.name} #{secretNoteId}";
|
||||
|
@ -108,18 +139,18 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
|
||||
// ring
|
||||
else if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring
|
||||
yield return this.TryCreate(ItemType.Ring, id, () => new Ring(id));
|
||||
yield return this.TryCreate(ItemType.Ring, id, p => new Ring(p.ID));
|
||||
|
||||
// item
|
||||
else
|
||||
{
|
||||
// spawn main item
|
||||
SObject item = null;
|
||||
yield return this.TryCreate(ItemType.Object, id, () =>
|
||||
yield return this.TryCreate(ItemType.Object, id, p =>
|
||||
{
|
||||
return item = (id == 812 // roe
|
||||
? new ColoredObject(id, 1, Color.White)
|
||||
: new SObject(id, 1)
|
||||
return item = (p.ID == 812 // roe
|
||||
? new ColoredObject(p.ID, 1, Color.White)
|
||||
: new SObject(p.ID, 1)
|
||||
);
|
||||
});
|
||||
if (item == null)
|
||||
|
@ -131,7 +162,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
// fruit products
|
||||
case SObject.FruitsCategory:
|
||||
// wine
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 2 + id, () => new SObject(348, 1)
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 2 + item.ParentSheetIndex, _ => new SObject(348, 1)
|
||||
{
|
||||
Name = $"{item.Name} Wine",
|
||||
Price = item.Price * 3,
|
||||
|
@ -140,7 +171,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
});
|
||||
|
||||
// jelly
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 3 + id, () => new SObject(344, 1)
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 3 + item.ParentSheetIndex, _ => new SObject(344, 1)
|
||||
{
|
||||
Name = $"{item.Name} Jelly",
|
||||
Price = 50 + item.Price * 2,
|
||||
|
@ -152,7 +183,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
// vegetable products
|
||||
case SObject.VegetableCategory:
|
||||
// juice
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 4 + id, () => new SObject(350, 1)
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 4 + item.ParentSheetIndex, _ => new SObject(350, 1)
|
||||
{
|
||||
Name = $"{item.Name} Juice",
|
||||
Price = (int)(item.Price * 2.25d),
|
||||
|
@ -161,7 +192,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
});
|
||||
|
||||
// pickled
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, () => new SObject(342, 1)
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + item.ParentSheetIndex, _ => new SObject(342, 1)
|
||||
{
|
||||
Name = $"Pickled {item.Name}",
|
||||
Price = 50 + item.Price * 2,
|
||||
|
@ -172,7 +203,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
|
||||
// flower honey
|
||||
case SObject.flowersCategory:
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, () =>
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + item.ParentSheetIndex, _ =>
|
||||
{
|
||||
SObject honey = new SObject(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false)
|
||||
{
|
||||
|
@ -185,40 +216,49 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
break;
|
||||
|
||||
// roe and aged roe (derived from FishPond.GetFishProduce)
|
||||
case SObject.sellAtFishShopCategory when id == 812:
|
||||
foreach (var pair in Game1.objectInformation)
|
||||
case SObject.sellAtFishShopCategory when item.ParentSheetIndex == 812:
|
||||
{
|
||||
// get input
|
||||
SObject input = this.TryCreate(ItemType.Object, -1, () => new SObject(pair.Key, 1))?.Item as SObject;
|
||||
if (input == null || input.Category != SObject.FishCategory)
|
||||
continue;
|
||||
Color color = this.GetRoeColor(input);
|
||||
this.GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags);
|
||||
|
||||
// yield roe
|
||||
SObject roe = null;
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + id, () =>
|
||||
foreach (var pair in Game1.objectInformation)
|
||||
{
|
||||
roe = new ColoredObject(812, 1, color)
|
||||
{
|
||||
name = $"{input.Name} Roe",
|
||||
preserve = { Value = SObject.PreserveType.Roe },
|
||||
preservedParentSheetIndex = { Value = input.ParentSheetIndex }
|
||||
};
|
||||
roe.Price += input.Price / 2;
|
||||
return roe;
|
||||
});
|
||||
// get input
|
||||
SObject input = this.TryCreate(ItemType.Object, pair.Key, p => new SObject(p.ID, 1))?.Item as SObject;
|
||||
var inputTags = input?.GetContextTags();
|
||||
if (inputTags?.Any() != true)
|
||||
continue;
|
||||
|
||||
// aged roe
|
||||
if (roe != null && pair.Key != 698) // aged sturgeon roe is caviar, which is a separate item
|
||||
{
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + id, () => new ColoredObject(447, 1, color)
|
||||
// check if roe-producing fish
|
||||
if (!inputTags.Any(tag => simpleTags.Contains(tag)) && !complexTags.Any(set => set.All(tag => input.HasContextTag(tag))))
|
||||
continue;
|
||||
|
||||
// yield roe
|
||||
SObject roe = null;
|
||||
Color color = this.GetRoeColor(input);
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + item.ParentSheetIndex, _ =>
|
||||
{
|
||||
name = $"Aged {input.Name} Roe",
|
||||
Category = -27,
|
||||
preserve = { Value = SObject.PreserveType.AgedRoe },
|
||||
preservedParentSheetIndex = { Value = input.ParentSheetIndex },
|
||||
Price = roe.Price * 2
|
||||
roe = new ColoredObject(812, 1, color)
|
||||
{
|
||||
name = $"{input.Name} Roe",
|
||||
preserve = { Value = SObject.PreserveType.Roe },
|
||||
preservedParentSheetIndex = { Value = input.ParentSheetIndex }
|
||||
};
|
||||
roe.Price += input.Price / 2;
|
||||
return roe;
|
||||
});
|
||||
|
||||
// aged roe
|
||||
if (roe != null && pair.Key != 698) // aged sturgeon roe is caviar, which is a separate item
|
||||
{
|
||||
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + item.ParentSheetIndex, _ => new ColoredObject(447, 1, color)
|
||||
{
|
||||
name = $"Aged {input.Name} Roe",
|
||||
Category = -27,
|
||||
preserve = { Value = SObject.PreserveType.AgedRoe },
|
||||
preservedParentSheetIndex = { Value = input.ParentSheetIndex },
|
||||
Price = roe.Price * 2
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -234,6 +274,26 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>Get optimized lookups to match items which produce roe in a fish pond.</summary>
|
||||
/// <param name="simpleTags">A lookup of simple singular tags which match a roe-producing fish.</param>
|
||||
/// <param name="complexTags">A list of tag sets which match roe-producing fish.</param>
|
||||
private void GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags)
|
||||
{
|
||||
simpleTags = new HashSet<string>();
|
||||
complexTags = new List<List<string>>();
|
||||
|
||||
foreach (FishPondData data in Game1.content.Load<List<FishPondData>>("Data\\FishPondData"))
|
||||
{
|
||||
if (data.ProducedItems.All(p => p.ItemID != 812))
|
||||
continue; // doesn't produce roe
|
||||
|
||||
if (data.RequiredTags.Count == 1 && !data.RequiredTags[0].StartsWith("!"))
|
||||
simpleTags.Add(data.RequiredTags[0]);
|
||||
else
|
||||
complexTags.Add(data.RequiredTags);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Try to load a data file, and return empty data if it's invalid.</summary>
|
||||
/// <typeparam name="TKey">The asset key type.</typeparam>
|
||||
/// <typeparam name="TValue">The asset value type.</typeparam>
|
||||
|
@ -255,13 +315,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
|
|||
/// <param name="type">The item type.</param>
|
||||
/// <param name="id">The unique ID (if different from the item's parent sheet index).</param>
|
||||
/// <param name="createItem">Create an item instance.</param>
|
||||
private SearchableItem TryCreate(ItemType type, int id, Func<Item> createItem)
|
||||
private SearchableItem TryCreate(ItemType type, int id, Func<SearchableItem, Item> createItem)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = createItem();
|
||||
item.getDescription(); // force-load item data, so it crashes here if it's invalid
|
||||
return new SearchableItem(type, id, item);
|
||||
var item = new SearchableItem(type, id, createItem);
|
||||
item.Item.getDescription(); // force-load item data, so it crashes here if it's invalid
|
||||
return item;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -47,16 +47,16 @@
|
|||
|
||||
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' != ''">
|
||||
<Reference Include="MonoGame.Framework">
|
||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\MonoGame.Framework.dll</HintPath>
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\MonoGame.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewModdingAPI">
|
||||
<HintPath>..\SMAPI\bin\Release\StardewModdingAPI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\StardewValley.dll</HintPath>
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewValley.GameData">
|
||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\StardewValley.GameData.dll</HintPath>
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\StardewValley.GameData.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"Name": "Console Commands",
|
||||
"Author": "SMAPI",
|
||||
"Version": "3.7.5",
|
||||
"Version": "3.7.6",
|
||||
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
||||
"UniqueID": "SMAPI.ConsoleCommands",
|
||||
"EntryDll": "ConsoleCommands.dll",
|
||||
"MinimumApiVersion": "3.7.5"
|
||||
"MinimumApiVersion": "3.7.6"
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<HintPath>..\SMAPI\bin\Release\StardewModdingAPI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\StardewValley.dll</HintPath>
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"Name": "Save Backup",
|
||||
"Author": "SMAPI",
|
||||
"Version": "3.7.5",
|
||||
"Version": "3.7.6",
|
||||
"Description": "Automatically backs up all your saves once per day into its folder.",
|
||||
"UniqueID": "SMAPI.SaveBackup",
|
||||
"EntryDll": "SaveBackup.dll",
|
||||
"MinimumApiVersion": "3.7.5"
|
||||
"MinimumApiVersion": "3.7.6"
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' == ''">
|
||||
<PackageReference Include="Moq" Version="4.14.6" />
|
||||
<PackageReference Include="Moq" Version="4.15.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' == ''">
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.24" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.28" />
|
||||
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.0.0" />
|
||||
<PackageReference Include="System.Management" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT' AND '$(TargetFramework)' == 'netstandard2.0'" />
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.6.0" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.14" />
|
||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.7.0" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.17" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.24" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.28" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
|
||||
<PackageReference Include="Markdig" Version="0.21.1" />
|
||||
<PackageReference Include="Markdig" Version="0.22.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.8" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.13" />
|
||||
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.1" />
|
||||
|
|
|
@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{EB35A917-6
|
|||
ProjectSection(SolutionItems) = preProject
|
||||
..\docs\mod-build-config.md = ..\docs\mod-build-config.md
|
||||
..\docs\README.md = ..\docs\README.md
|
||||
..\docs\release-notes-archived.md = ..\docs\release-notes-archived.md
|
||||
..\docs\release-notes.md = ..\docs\release-notes.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
|
|
@ -72,9 +72,9 @@ namespace StardewModdingAPI
|
|||
****/
|
||||
/// <summary>SMAPI's current semantic version.</summary>
|
||||
#if !SMAPI_FOR_MOBILE
|
||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.7.5");
|
||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.7.6");
|
||||
#else
|
||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.7.5");
|
||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.7.6");
|
||||
|
||||
/// <summary>Android SMAPI's current semantic version.</summary>
|
||||
public static ISemanticVersion AndroidApiVersion { get; } = new Toolkit.SemanticVersion("0.8.8");
|
||||
|
|
|
@ -112,9 +112,9 @@ namespace StardewModdingAPI.Framework.Commands
|
|||
private IEnumerable<SearchResult> GetAllPatches()
|
||||
{
|
||||
#if HARMONY_2
|
||||
foreach (MethodBase method in Harmony.GetAllPatchedMethods())
|
||||
foreach (MethodBase method in Harmony.GetAllPatchedMethods().ToArray())
|
||||
#else
|
||||
foreach (MethodBase method in this.HarmonyInstance.GetPatchedMethods())
|
||||
foreach (MethodBase method in this.HarmonyInstance.GetPatchedMethods().ToArray())
|
||||
#endif
|
||||
{
|
||||
// get metadata for method
|
||||
|
|
|
@ -199,9 +199,10 @@ namespace StardewModdingAPI.Framework.Logging
|
|||
|
||||
/// <summary>Write an update alert marker file.</summary>
|
||||
/// <param name="version">The new version found.</param>
|
||||
public void WriteUpdateMarker(string version)
|
||||
/// <param name="url">The download URL for the update.</param>
|
||||
public void WriteUpdateMarker(string version, string url)
|
||||
{
|
||||
File.WriteAllText(Constants.UpdateMarker, version);
|
||||
File.WriteAllText(Constants.UpdateMarker, $"{version}|{url}");
|
||||
}
|
||||
|
||||
/// <summary>Check whether SMAPI crashed or detected an update during the last session, and display them in the SMAPI console.</summary>
|
||||
|
@ -210,13 +211,17 @@ namespace StardewModdingAPI.Framework.Logging
|
|||
// show update alert
|
||||
if (File.Exists(Constants.UpdateMarker))
|
||||
{
|
||||
string rawUpdateFound = File.ReadAllText(Constants.UpdateMarker);
|
||||
if (SemanticVersion.TryParse(rawUpdateFound, out ISemanticVersion updateFound))
|
||||
string[] rawUpdateFound = File.ReadAllText(Constants.UpdateMarker).Split(new [] { '|' }, 2);
|
||||
if (SemanticVersion.TryParse(rawUpdateFound[0], out ISemanticVersion updateFound))
|
||||
{
|
||||
if (Constants.ApiVersion.IsPrerelease() && updateFound.IsNewerThan(Constants.ApiVersion))
|
||||
{
|
||||
string url = rawUpdateFound.Length > 1
|
||||
? rawUpdateFound[1]
|
||||
: Constants.HomePageUrl;
|
||||
|
||||
this.Monitor.Log("A new version of SMAPI was detected last time you played.", LogLevel.Error);
|
||||
this.Monitor.Log($"You can update to {updateFound}: https://smapi.io.", LogLevel.Error);
|
||||
this.Monitor.Log($"You can update to {updateFound}: {url}.", LogLevel.Error);
|
||||
#if !SMAPI_FOR_MOBILE
|
||||
this.Monitor.Log("Press any key to continue playing anyway. (This only appears when using a SMAPI beta.)", LogLevel.Info);
|
||||
Console.ReadKey();
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
|||
private bool TryRewriteToProperty(ModuleDefinition module, Instruction instruction, FieldReference fieldRef, TypeDefinition declaringType, bool isRead)
|
||||
{
|
||||
// get equivalent property
|
||||
PropertyDefinition property = declaringType.Properties.FirstOrDefault(p => p.Name == fieldRef.Name);
|
||||
PropertyDefinition property = declaringType?.Properties.FirstOrDefault(p => p.Name == fieldRef.Name);
|
||||
MethodDefinition method = isRead ? property?.GetMethod : property?.SetMethod;
|
||||
if (method == null)
|
||||
return false;
|
||||
|
|
|
@ -1318,40 +1318,46 @@ namespace StardewModdingAPI.Framework
|
|||
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
|
||||
this.Monitor.Log("Checking for updates...");
|
||||
|
||||
// check SMAPI version
|
||||
#if !SMAPI_FOR_MOBILE
|
||||
ISemanticVersion updateFound = null;
|
||||
try
|
||||
// check SMAPI version
|
||||
{
|
||||
// fetch update check
|
||||
ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Pathoschild.SMAPI", Constants.ApiVersion, new[] { $"GitHub:{this.Settings.GitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value;
|
||||
if (response.SuggestedUpdate != null)
|
||||
this.Monitor.Log($"You can update SMAPI to {response.SuggestedUpdate.Version}: {Constants.HomePageUrl}", LogLevel.Alert);
|
||||
else
|
||||
this.Monitor.Log(" SMAPI okay.");
|
||||
|
||||
updateFound = response.SuggestedUpdate?.Version;
|
||||
|
||||
// show errors
|
||||
if (response.Errors.Any())
|
||||
ISemanticVersion updateFound = null;
|
||||
string updateUrl = null;
|
||||
try
|
||||
{
|
||||
this.Monitor.Log("Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||
this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Monitor.Log("Couldn't check for a new version of SMAPI. This won't affect your game, but you won't be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||
this.Monitor.Log(ex is WebException && ex.InnerException == null
|
||||
? $"Error: {ex.Message}"
|
||||
: $"Error: {ex.GetLogSummary()}"
|
||||
);
|
||||
}
|
||||
// fetch update check
|
||||
ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Pathoschild.SMAPI", Constants.ApiVersion, new[] { $"GitHub:{this.Settings.GitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value;
|
||||
updateFound = response.SuggestedUpdate?.Version;
|
||||
updateUrl = response.SuggestedUpdate?.Url ?? Constants.HomePageUrl;
|
||||
|
||||
// show update message on next launch
|
||||
if (updateFound != null)
|
||||
this.LogManager.WriteUpdateMarker(updateFound.ToString());
|
||||
// log message
|
||||
if (updateFound != null)
|
||||
this.Monitor.Log($"You can update SMAPI to {updateFound}: {updateUrl}", LogLevel.Alert);
|
||||
else
|
||||
this.Monitor.Log(" SMAPI okay.");
|
||||
|
||||
// show errors
|
||||
if (response.Errors.Any())
|
||||
{
|
||||
this.Monitor.Log("Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||
this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Monitor.Log("Couldn't check for a new version of SMAPI. This won't affect your game, but you won't be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||
this.Monitor.Log(ex is WebException && ex.InnerException == null
|
||||
? $"Error: {ex.Message}"
|
||||
: $"Error: {ex.GetLogSummary()}"
|
||||
);
|
||||
}
|
||||
|
||||
// show update message on next launch
|
||||
if (updateFound != null)
|
||||
this.LogManager.WriteUpdateMarker(updateFound.ToString(), updateUrl);
|
||||
}
|
||||
#endif
|
||||
|
||||
// check mod versions
|
||||
if (mods.Any())
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<PackageReference Include="LargeAddressAware" Version="1.0.5" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Platonymous.TMXTile" Version="1.5.6" />
|
||||
<PackageReference Include="Platonymous.TMXTile" Version="1.5.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' == ''">
|
||||
|
@ -135,27 +135,18 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_GOOGLE_MONOANDROID10'))">
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewValley.GameData">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\StardewValley.GameData.dll</HintPath>
|
||||
</Reference>
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="xTile">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.151\assemblies_decrypt\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_GOOGLE_145'))">
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -171,9 +162,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_GOOGLE_LEGACY'))">
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.4.128\assemblies\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -186,9 +174,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_SAMSUNG'))">
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\samsung_base_1.4.5.144\assemblies\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\samsung_base_1.4.5.144\assemblies\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -204,9 +189,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_AMAZON'))">
|
||||
<!-- <Reference Include="MonoGame.Framework">-->
|
||||
<!-- <HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\amazon_base_1.4.5.144\assemblies\MonoGame.Framework.dll</HintPath>-->
|
||||
<!-- </Reference>-->
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\..\..\Downloads\StardewValleyAndroidStuff\amazon_base_1.4.5.144\assemblies\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -249,10 +231,10 @@
|
|||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="SMAPI.Toolkit">
|
||||
<HintPath>..\SMAPI.Toolkit\bin\Release\net4.5\SMAPI.Toolkit.dll</HintPath>
|
||||
<HintPath>..\SMAPI.Toolkit\bin\Debug\net4.5\SMAPI.Toolkit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SMAPI.Toolkit.CoreInterfaces">
|
||||
<HintPath>..\SMAPI.Toolkit.CoreInterfaces\bin\Release\net4.5\SMAPI.Toolkit.CoreInterfaces.dll</HintPath>
|
||||
<HintPath>..\SMAPI.Toolkit.CoreInterfaces\bin\Debug\net4.5\SMAPI.Toolkit.CoreInterfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="TMXTile">
|
||||
<HintPath>..\Loader\libs\TMXTile.dll</HintPath>
|
||||
|
|
|
@ -29,7 +29,7 @@ using Thread = System.Threading.Thread;
|
|||
namespace StardewModdingAPI
|
||||
{
|
||||
[Activity(Label = "SMAPI Stardew Valley", Icon = "@mipmap/ic_launcher", Theme = "@style/Theme.Splash", MainLauncher = true, AlwaysRetainTaskState = true, LaunchMode = LaunchMode.SingleInstance, ScreenOrientation = ScreenOrientation.SensorLandscape, ConfigurationChanges = (ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.Orientation | ConfigChanges.ScreenLayout | ConfigChanges.ScreenSize | ConfigChanges.UiMode))]
|
||||
#if ANDROID_TARGET_GOOGLE
|
||||
#if !ANDROID_TARGET_GOOGLE
|
||||
public class SMainActivity: MainActivity
|
||||
#else
|
||||
public class SMainActivity : MainActivity, ILicenseCheckerCallback, IJavaObject, IDisposable, IJavaPeerable
|
||||
|
|
Loading…
Reference in New Issue