make item spawn commands more robust

This commit is contained in:
Jesse Plamondon-Willard 2019-10-28 15:59:12 -04:00
parent 08dc846195
commit be79a04206
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
2 changed files with 205 additions and 148 deletions

View File

@ -57,6 +57,8 @@ For modders:
* Updated SMAPI/game version map. * Updated SMAPI/game version map.
* Fixes: * Fixes:
* Fixed mods needing to load custom `Map` assets before the game accesses them (SMAPI will now do so automatically). * Fixed mods needing to load custom `Map` assets before the game accesses them (SMAPI will now do so automatically).
* Fixed Console Commands not including upgraded tools in item commands.
* Fixed Console Commands' item commands failing if a mod adds invalid item data.
* Fixed Save Backup not pruning old backups if they're uncompressed. * Fixed Save Backup not pruning old backups if they're uncompressed.
* Fixed issues when a farmhand reconnects before the game notices they're disconnected. * Fixed issues when a farmhand reconnects before the game notices they're disconnected.
* Fixed 'received message' logs shown in non-developer mode. * Fixed 'received message' logs shown in non-developer mode.

View File

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData; using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
using StardewValley; using StardewValley;
@ -22,70 +25,76 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
** Public methods ** Public methods
*********/ *********/
/// <summary>Get all spawnable items.</summary> /// <summary>Get all spawnable items.</summary>
[SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "TryCreate invokes the lambda immediately.")]
public IEnumerable<SearchableItem> GetAll() public IEnumerable<SearchableItem> GetAll()
{
IEnumerable<SearchableItem> GetAllRaw()
{ {
// get tools // get tools
yield return new SearchableItem(ItemType.Tool, ToolFactory.axe, ToolFactory.getToolFromDescription(ToolFactory.axe, 0)); for (int quality = Tool.stone; quality <= Tool.iridium; quality++)
yield return new SearchableItem(ItemType.Tool, ToolFactory.hoe, ToolFactory.getToolFromDescription(ToolFactory.hoe, 0)); {
yield return new SearchableItem(ItemType.Tool, ToolFactory.pickAxe, ToolFactory.getToolFromDescription(ToolFactory.pickAxe, 0)); yield return this.TryCreate(ItemType.Tool, ToolFactory.axe, () => ToolFactory.getToolFromDescription(ToolFactory.axe, quality));
yield return new SearchableItem(ItemType.Tool, ToolFactory.wateringCan, ToolFactory.getToolFromDescription(ToolFactory.wateringCan, 0)); yield return this.TryCreate(ItemType.Tool, ToolFactory.hoe, () => ToolFactory.getToolFromDescription(ToolFactory.hoe, quality));
yield return new SearchableItem(ItemType.Tool, ToolFactory.fishingRod, ToolFactory.getToolFromDescription(ToolFactory.fishingRod, 0)); yield return this.TryCreate(ItemType.Tool, ToolFactory.pickAxe, () => ToolFactory.getToolFromDescription(ToolFactory.pickAxe, quality));
yield return new SearchableItem(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, ToolFactory.wateringCan, () => ToolFactory.getToolFromDescription(ToolFactory.wateringCan, quality));
yield return new SearchableItem(ItemType.Tool, this.CustomIDOffset + 1, new Shears()); if (quality != Tool.iridium)
yield return new SearchableItem(ItemType.Tool, this.CustomIDOffset + 2, new Pan()); yield return this.TryCreate(ItemType.Tool, ToolFactory.fishingRod, () => ToolFactory.getToolFromDescription(ToolFactory.fishingRod, quality));
yield return new SearchableItem(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
// clothing yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 1, () => new Shears());
foreach (int id in Game1.clothingInformation.Keys) yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 2, () => new Pan());
yield return new SearchableItem(ItemType.Clothing, id, new Clothing(id)); yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 3, () => new Wand());
// wallpapers // wallpapers
for (int id = 0; id < 112; id++) for (int id = 0; id < 112; id++)
yield return new SearchableItem(ItemType.Wallpaper, id, new Wallpaper(id) { Category = SObject.furnitureCategory }); yield return this.TryCreate(ItemType.Wallpaper, id, () => new Wallpaper(id) { Category = SObject.furnitureCategory });
// flooring // flooring
for (int id = 0; id < 40; id++) for (int id = 0; id < 40; id++)
yield return new SearchableItem(ItemType.Flooring, id, new Wallpaper(id, isFloor: true) { Category = SObject.furnitureCategory }); yield return this.TryCreate(ItemType.Flooring, id, () => new Wallpaper(id, isFloor: true) { Category = SObject.furnitureCategory });
// equipment // equipment
foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\Boots").Keys) foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\Boots").Keys)
yield return new SearchableItem(ItemType.Boots, id, new Boots(id)); yield return this.TryCreate(ItemType.Boots, id, () => new Boots(id));
foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\hats").Keys) foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\hats").Keys)
yield return new SearchableItem(ItemType.Hat, id, new Hat(id)); yield return this.TryCreate(ItemType.Hat, id, () => new Hat(id));
foreach (int id in Game1.objectInformation.Keys) foreach (int id in Game1.objectInformation.Keys)
{ {
if (id >= Ring.ringLowerIndexRange && id <= Ring.ringUpperIndexRange) if (id >= Ring.ringLowerIndexRange && id <= Ring.ringUpperIndexRange)
yield return new SearchableItem(ItemType.Ring, id, new Ring(id)); yield return this.TryCreate(ItemType.Ring, id, () => new Ring(id));
} }
// weapons // weapons
foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\weapons").Keys) foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\weapons").Keys)
{ {
Item weapon = (id >= 32 && id <= 34) yield return this.TryCreate(ItemType.Weapon, id, () => (id >= 32 && id <= 34)
? (Item)new Slingshot(id) ? (Item)new Slingshot(id)
: new MeleeWeapon(id); : new MeleeWeapon(id)
yield return new SearchableItem(ItemType.Weapon, id, weapon); );
} }
// furniture // furniture
foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\Furniture").Keys) foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\Furniture").Keys)
{ {
if (id == 1466 || id == 1468) if (id == 1466 || id == 1468)
yield return new SearchableItem(ItemType.Furniture, id, new TV(id, Vector2.Zero)); yield return this.TryCreate(ItemType.Furniture, id, () => new TV(id, Vector2.Zero));
else else
yield return new SearchableItem(ItemType.Furniture, id, new Furniture(id, Vector2.Zero)); yield return this.TryCreate(ItemType.Furniture, id, () => new Furniture(id, Vector2.Zero));
} }
// craftables // craftables
foreach (int id in Game1.bigCraftablesInformation.Keys) foreach (int id in Game1.bigCraftablesInformation.Keys)
yield return new SearchableItem(ItemType.BigCraftable, id, new SObject(Vector2.Zero, id)); yield return this.TryCreate(ItemType.BigCraftable, id, () => new SObject(Vector2.Zero, id));
// secret notes // secret notes
foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\SecretNotes").Keys) foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\SecretNotes").Keys)
{
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + id, () =>
{ {
SObject note = new SObject(79, 1); SObject note = new SObject(79, 1);
note.name = $"{note.name} #{id}"; note.name = $"{note.name} #{id}";
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset + id, note); return note;
});
} }
// objects // objects
@ -96,13 +105,22 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
if (id >= Ring.ringLowerIndexRange && id <= Ring.ringUpperIndexRange) if (id >= Ring.ringLowerIndexRange && id <= Ring.ringUpperIndexRange)
continue; // handled separated continue; // handled separated
SObject item = new SObject(id, 1); // spawn main item
yield return new SearchableItem(ItemType.Object, id, item); SObject item;
{
SearchableItem main = this.TryCreate(ItemType.Object, id, () => new SObject(id, 1));
yield return main;
item = main?.Item as SObject;
}
if (item == null)
continue;
// fruit products // fruit products
if (item.Category == SObject.FruitsCategory) if (item.Category == SObject.FruitsCategory)
{ {
// wine // wine
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 2 + id, () =>
{
SObject wine = new SObject(348, 1) SObject wine = new SObject(348, 1)
{ {
Name = $"{item.Name} Wine", Name = $"{item.Name} Wine",
@ -110,9 +128,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}; };
wine.preserve.Value = SObject.PreserveType.Wine; wine.preserve.Value = SObject.PreserveType.Wine;
wine.preservedParentSheetIndex.Value = item.ParentSheetIndex; wine.preservedParentSheetIndex.Value = item.ParentSheetIndex;
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset * 2 + id, wine); return wine;
});
// jelly // jelly
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 3 + id, () =>
{
SObject jelly = new SObject(344, 1) SObject jelly = new SObject(344, 1)
{ {
Name = $"{item.Name} Jelly", Name = $"{item.Name} Jelly",
@ -120,13 +141,16 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}; };
jelly.preserve.Value = SObject.PreserveType.Jelly; jelly.preserve.Value = SObject.PreserveType.Jelly;
jelly.preservedParentSheetIndex.Value = item.ParentSheetIndex; jelly.preservedParentSheetIndex.Value = item.ParentSheetIndex;
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset * 3 + id, jelly); return jelly;
});
} }
// vegetable products // vegetable products
else if (item.Category == SObject.VegetableCategory) else if (item.Category == SObject.VegetableCategory)
{ {
// juice // juice
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 4 + id, () =>
{
SObject juice = new SObject(350, 1) SObject juice = new SObject(350, 1)
{ {
Name = $"{item.Name} Juice", Name = $"{item.Name} Juice",
@ -134,9 +158,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}; };
juice.preserve.Value = SObject.PreserveType.Juice; juice.preserve.Value = SObject.PreserveType.Juice;
juice.preservedParentSheetIndex.Value = item.ParentSheetIndex; juice.preservedParentSheetIndex.Value = item.ParentSheetIndex;
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset * 4 + id, juice); return juice;
});
// pickled // pickled
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, () =>
{
SObject pickled = new SObject(342, 1) SObject pickled = new SObject(342, 1)
{ {
Name = $"Pickled {item.Name}", Name = $"Pickled {item.Name}",
@ -144,7 +171,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}; };
pickled.preserve.Value = SObject.PreserveType.Pickle; pickled.preserve.Value = SObject.PreserveType.Pickle;
pickled.preservedParentSheetIndex.Value = item.ParentSheetIndex; pickled.preservedParentSheetIndex.Value = item.ParentSheetIndex;
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset * 5 + id, pickled); return pickled;
});
} }
// flower honey // flower honey
@ -176,6 +204,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
// yield honey // yield honey
if (type != null) if (type != null)
{
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, () =>
{ {
SObject honey = new SObject(Vector2.Zero, 340, item.Name + " Honey", false, true, false, false) SObject honey = new SObject(Vector2.Zero, 340, item.Name + " Honey", false, true, false, false)
{ {
@ -188,10 +218,35 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
honey.Name = $"{item.Name} Honey"; honey.Name = $"{item.Name} Honey";
honey.Price += item.Price * 2; honey.Price += item.Price * 2;
} }
yield return new SearchableItem(ItemType.Object, this.CustomIDOffset * 5 + id, honey);
return honey;
});
} }
} }
} }
} }
return GetAllRaw().Where(p => p != null);
}
/*********
** Private methods
*********/
/// <summary>Create a searchable item if valid.</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="createItem">Create an item instance.</param>
private SearchableItem TryCreate(ItemType type, int id, Func<Item> createItem)
{
try
{
return new SearchableItem(type, id, createItem());
}
catch
{
return null; // if some item data is invalid, just don't include it
}
}
} }
} }