From 02f098dd89bdd45a604446d24ab042754c52acab Mon Sep 17 00:00:00 2001 From: b-b-blueberry Date: Mon, 16 Aug 2021 17:38:02 +1000 Subject: [PATCH] RELEASE 1.0.0: Item names can now correctly be used as identifiers in content pack RecipeIngredients entries. Minor changes and cleaning-up for initial release. --- RaisedGardenBeds/AssetManager.cs | 18 +++--- RaisedGardenBeds/HarmonyPatches.cs | 92 ++++------------------------- RaisedGardenBeds/ItemDefinition.cs | 12 ++-- RaisedGardenBeds/OutdoorPot.cs | 56 ++++++++++++++++-- RaisedGardenBeds/manifest.json | 2 +- [RGB] RaisedGardenBeds/content.json | 34 +++++------ 6 files changed, 96 insertions(+), 118 deletions(-) diff --git a/RaisedGardenBeds/AssetManager.cs b/RaisedGardenBeds/AssetManager.cs index 014ac86..32120d0 100644 --- a/RaisedGardenBeds/AssetManager.cs +++ b/RaisedGardenBeds/AssetManager.cs @@ -11,17 +11,19 @@ public class AssetManager : IAssetLoader, IAssetEditor { private readonly IModHelper _helper; - internal static readonly string AssetPrefix = Path.Combine("Mods", "blueberry.rgb.Assets"); + internal static readonly string GameContentAssetPath = Path.Combine("Mods", "blueberry.rgb.Assets"); - internal static readonly string GameContentEndOfNightSpritesPath = Path.Combine(AssetPrefix, "EndOfNightSprites"); - internal static readonly string GameContentEventDataPath = Path.Combine(AssetPrefix, "EventData"); - internal static readonly string GameContentCommonTranslationDataPath = Path.Combine(AssetPrefix, "CommonTranslations"); - internal static readonly string GameContentItemTranslationDataPath = Path.Combine(AssetPrefix, "ItemTranslations"); + internal static readonly string GameContentEndOfNightSpritesPath = Path.Combine(GameContentAssetPath, "EndOfNightSprites"); + internal static readonly string GameContentEventDataPath = Path.Combine(GameContentAssetPath, "EventData"); + internal static readonly string GameContentCommonTranslationDataPath = Path.Combine(GameContentAssetPath, "CommonTranslations"); + internal static readonly string GameContentItemTranslationDataPath = Path.Combine(GameContentAssetPath, "ItemTranslations"); - internal static readonly string LocalEndOfNightSpritesPath = Path.Combine("assets", "endOfNightSprites.png"); - internal static readonly string LocalEventDataPath = Path.Combine("assets", "eventData.json"); + internal static readonly string LocalAssetPath = "assets"; - internal static readonly string ContentPackPath = Path.Combine("assets", "ContentPack"); + internal static readonly string LocalEndOfNightSpritesPath = Path.Combine(LocalAssetPath, "endOfNightSprites.png"); + internal static readonly string LocalEventDataPath = Path.Combine(LocalAssetPath, "eventData.json"); + + internal static readonly string ContentPackPath = Path.Combine(LocalAssetPath, "ContentPack"); public AssetManager(IModHelper helper) diff --git a/RaisedGardenBeds/HarmonyPatches.cs b/RaisedGardenBeds/HarmonyPatches.cs index 5418e88..c7fdfa5 100644 --- a/RaisedGardenBeds/HarmonyPatches.cs +++ b/RaisedGardenBeds/HarmonyPatches.cs @@ -3,7 +3,6 @@ using StardewValley; using StardewValley.Menus; using System; -using System.Collections.Generic; using System.Linq; namespace RaisedGardenBeds @@ -42,57 +41,16 @@ internal static void Patch(string id) harmony.Patch( original: AccessTools.Method(typeof(CraftingPage), "clickCraftingRecipe"), prefix: new HarmonyMethod(typeof(HarmonyPatches), nameof(CraftingPage_ClickCraftingRecipe_Prefix))); - /* - harmony.Patch( - original: AccessTools.Method(typeof(CraftingRecipe), "drawMenuView"), - postfix: new HarmonyMethod(typeof(HarmonyPatches), nameof(CraftingRecipe_DrawMenuView_Postfix))); - */ - - // DISPLAY NAME TRANSPILER - /* - var sub = AccessTools.Constructor(type: typeof(CraftingRecipe), parameters: new Type[] { typeof(string), typeof(bool) }); - var dom = new HarmonyMethod(typeof(HarmonyPatches), nameof(Transpile_CraftingRecipeConstructor)); - harmony.Patch( - original: sub, - transpiler: dom - ); - */ } private static void ErrorHandler(Exception e) { - Log.E($"{ModEntry.Instance.ModManifest.UniqueID} failed in harmony prefix.{Environment.NewLine}{e}"); - } - /* - public static IEnumerable Transpile_CraftingRecipeConstructor(ILGenerator gen, MethodBase original, IEnumerable instructions) - { - foreach (CodeInstruction i in instructions) - { - if (i.opcode != OpCodes.Call || !(i.operand is MethodInfo method && method.Name == "get_CurrentLanguageCode")) - { - yield return i; - } - else - { - // Original: Call LocalizedContentManager.get_CurrentLanguageCode() - // Goal: Call HarmonyPatches.CheckDisplayName(int, bool) - - yield return new CodeInstruction(OpCodes.Ldloc_1, null) { labels = i.labels }; // infoSplit - yield return new CodeInstruction(OpCodes.Ldlen, null); // Length - yield return new CodeInstruction(OpCodes.Conv_I4, null); // (int) - - yield return new CodeInstruction(OpCodes.Ldarg_2, null); // isCookingRecipe - - yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(HarmonyPatches), nameof(CheckDisplayName))); - } - } - } - - public static int CheckDisplayName(int infoSplitLength, bool isCookingRecipe) - { - return infoSplitLength < (isCookingRecipe ? 5 : 6) ? 0 : 1; + Log.E($"{ModEntry.Instance.ModManifest.UniqueID} failed in harmony patch method.{Environment.NewLine}{e}"); } - */ + + /// + /// Replace logic determining item drop-in actions on garden bed objects. + /// public static bool Utility_IsThereAnObjectHereWhichAcceptsThisItem_Prefix( ref bool __result, GameLocation location, @@ -123,6 +81,9 @@ public static bool Utility_IsThereAnObjectHereWhichAcceptsThisItem_Prefix( return true; } + /// + /// Add logic to consider new conditions for planting seeds in garden bed objects. + /// public static bool Utility_IsViableSeedSpot_Prefix( GameLocation location, Vector2 tileLocation, @@ -132,11 +93,7 @@ public static bool Utility_IsViableSeedSpot_Prefix( { if (location.Objects.TryGetValue(tileLocation, out StardewValley.Object o) && o != null && o is OutdoorPot op) { - if (OutdoorPot.CanAcceptItemOrSeed(item) && OutdoorPot.CanAcceptSeed(item: item, op: op) && OutdoorPot.CanAcceptAnything(op: op)) - { - return true; - } - return false; + return OutdoorPot.CanAcceptItemOrSeed(item) && OutdoorPot.CanAcceptSeed(item: item, op: op) && OutdoorPot.CanAcceptAnything(op: op); } } catch (Exception e) @@ -146,6 +103,9 @@ public static bool Utility_IsViableSeedSpot_Prefix( return true; } + /// + /// Replace logic for garden bed objects being watered by sprinklers. + /// public static bool Object_ApplySprinkler_Prefix( GameLocation location, Vector2 tile) @@ -266,33 +226,5 @@ public static bool CraftingPage_ClickCraftingRecipe_Prefix( } return true; } - /* - /// - /// - /// - public static void CraftingRecipe_DrawMenuView_Postfix( - CraftingRecipe __instance, - Microsoft.Xna.Framework.Graphics.SpriteBatch b, - int x, - int y, - float layerDepth) - { - if (__instance.name.StartsWith(OutdoorPot.GenericName)) - { - string variantKey = OutdoorPot.GetVariantKeyFromName(name: __instance.name); - Utility.drawWithShadow( - b, - texture: ModEntry.Sprites[ModEntry.ItemDefinitions[variantKey].SpriteKey], - position: new Vector2(x, y), - sourceRect: OutdoorPot.GetSourceRectangle(spriteIndex: ModEntry.ItemDefinitions[variantKey].SpriteIndex), - color: Color.White, - rotation: 0f, - origin: Vector2.Zero, - scale: Game1.pixelZoom, - flipped: false, - layerDepth: layerDepth); - } - } - */ } } diff --git a/RaisedGardenBeds/ItemDefinition.cs b/RaisedGardenBeds/ItemDefinition.cs index f557891..b28567b 100644 --- a/RaisedGardenBeds/ItemDefinition.cs +++ b/RaisedGardenBeds/ItemDefinition.cs @@ -98,12 +98,12 @@ internal static string ParseRecipeIngredients(ItemDefinition data) List ingredients = new List(); foreach (Dictionary entry in data.RecipeIngredients) { - int id = int.TryParse(entry["Object"], out int rawId) - // Base game objects must be referenced by ID - ? rawId - // Json Assets objects may be referenced by name - : Game1.objectInformation.Keys.FirstOrDefault - (key => key >= 2000 && Game1.objectInformation[key].Split('/')[0] == entry["Object"]); + string strId = entry["Object"]; + int id = int.TryParse(strId, out int intId) + // Base game objects may be referenced by ID + ? intId + // Base and Json Assets objects may be referenced by name + : Utility.fuzzyItemSearch(query: strId)?.ParentSheetIndex ?? -1; int quantity = int.Parse(entry["Count"]); ingredients.Add($"{id} {quantity}"); } diff --git a/RaisedGardenBeds/OutdoorPot.cs b/RaisedGardenBeds/OutdoorPot.cs index 5ea4652..f7dddaf 100644 --- a/RaisedGardenBeds/OutdoorPot.cs +++ b/RaisedGardenBeds/OutdoorPot.cs @@ -224,6 +224,9 @@ private void Event_VariantKeyChanged(NetString field, string oldValue, string ne } } + /// + /// Get a KeyValuePair containing the sprite key and index to be used with the common spritesheet. + /// public static KeyValuePair GetSpriteFromVariantKey(string variantKey) { return new KeyValuePair(ModEntry.ItemDefinitions[variantKey].SpriteKey, ModEntry.ItemDefinitions[variantKey].SpriteIndex); @@ -266,16 +269,25 @@ public static string GetDisplayNameFromRecipeName(string recipeName) return OutdoorPot.GetDisplayNameFromName(recipeName.Split('.').Last()); } + /// + /// Get shared localised description for garden bed objects. + /// public static string GetRawDescription() { return Translations.GetTranslation($"item.description{(ModEntry.Config.CanBePlacedInBuildings ? ".indoors" : "")}"); } + /// + /// Get localised display name for this object variant. + /// protected override string loadDisplayName() { return OutdoorPot.GetDisplayNameFromVariantKey(this.VariantKey.Value); } + /// + /// Get localised description string to fit object hovered-in-inventory popout box. + /// public override string getDescription() { string description = OutdoorPot.GetRawDescription(); @@ -460,7 +472,7 @@ public override void ApplySprinklerAnimation(GameLocation location) Vector2 position = (this.TileLocation * Game1.tileSize) - new Vector2(0, this.SoilHeightAboveGround * Game1.pixelZoom); int delay = Game1.random.Next(1000); - float id = (this.tileLocation.X * 4000) + this.tileLocation.Y; + float id = (this.TileLocation.X * 4000) + this.TileLocation.Y; Color colour = Color.White * 0.4f; const int frames = 4; const int interval = 60; @@ -821,13 +833,20 @@ public static bool CanAcceptSeed(Item item, OutdoorPot op) { return op.hoeDirt.Value.canPlantThisSeedHere(item.ParentSheetIndex, (int)op.TileLocation.X, (int)op.TileLocation.Y); } - + + /// + /// Set this object's held object to a given item. + /// public void HoldItem(Item item) { this.heldObject.Value = item.getOne() as StardewValley.Object; this.heldObject.Value.TileLocation = this.TileLocation; } + /// + /// + /// + /// If true, ejects the held object as debris regardless of any other conditions. public bool PopHeldItem(bool force = false) { bool popped = false; @@ -859,22 +878,36 @@ public bool PopHeldItem(bool force = false) return popped || (this.hoeDirt.Value.crop == null && this.heldObject.Value == null); } + /// + /// Check whether this object's held object is a sprinkler. + /// public bool IsHoldingSprinkler() { return this.heldObject.Value != null && this.heldObject.Value.IsSprinkler(); } + /// + /// Get the current radius for this object's held sprinkler, if any. Returns -1 if none is held. + /// public int GetSprinklerRadius() { return this.IsHoldingSprinkler() ? this.heldObject.Value.GetModifiedRadiusForSprinkler() : -1; } + /// + /// Water the garden bed's hoe dirt and any held crops. + /// public void Water() { this.hoeDirt.Value.state.Value = 1; this.showNextIndex.Value = this.hoeDirt.Value.state.Value == 1; } + /// + /// + /// + /// + /// public void Unbreak(GameLocation location = null, bool adjust = false) { this.BreakageTimer.Value = this.BreakageStart; @@ -937,9 +970,13 @@ public static void ArrangeAll(GameLocation specificLocation = null) location.Objects.Values.OfType().ToList().ForEach(o => o.Arrange(location: location)); } + /// + /// Considers neighbouring objects and sets this object's corner sprites to tile/tesselate/pattern with its neighbours to form arrangements. + /// + /// Specific location whose dictionary contains this object. public void Arrange(GameLocation location) { - if (!this.CanBeArranged) + if (!this.CanBeArranged || location == null) return; for (int i = 0; i < 4; ++i) @@ -964,13 +1001,17 @@ public void Arrange(GameLocation location) } } - public static void ArrangeWithNeighbours(GameLocation location, Vector2 tileLocation, int radius = 1) + /// + /// Identifies any garden bed objects on the given tile and tries to reform arrangements with neighbouring objects. + /// + /// Specific location to check within. Defaults to player's current location. + /// Tile location to check for objects. + public static void ArrangeWithNeighbours(GameLocation location, Vector2 tileLocation) { if (location == null) location = Game1.currentLocation; - if (radius < 1) - return; + const int radius = 1; Point origin = Utility.Vector2ToPoint(tileLocation); Point start = new Point( Math.Max(0, origin.X - radius), @@ -992,6 +1033,9 @@ public static void ArrangeWithNeighbours(GameLocation location, Vector2 tileLoca } } + /// + /// Whether any two objects are garden beds that can be grouped into arrangements. + /// private static bool CanBeArrangedWithNeighbour(OutdoorPot p, StardewValley.Object o) { bool facts = o is OutdoorPot op && op != null && op.canStackWith(p) && !op.IsBroken && op.CanBeArranged && p.CanBeArranged; diff --git a/RaisedGardenBeds/manifest.json b/RaisedGardenBeds/manifest.json index daed392..fd35811 100644 --- a/RaisedGardenBeds/manifest.json +++ b/RaisedGardenBeds/manifest.json @@ -1,7 +1,7 @@ { "Name": "Raised Garden Beds", "Author": "blueberry", - "Version": "1.0.0-prerelease.5", + "Version": "1.0.0", "Description": "Garden beds for your farm, usable outdoors with seasonal crops.", "UniqueID": "blueberry.RaisedGardenBeds", "EntryDll": "RaisedGardenBeds.dll", diff --git a/[RGB] RaisedGardenBeds/content.json b/[RGB] RaisedGardenBeds/content.json index ab6a435..93633c6 100644 --- a/[RGB] RaisedGardenBeds/content.json +++ b/[RGB] RaisedGardenBeds/content.json @@ -3,7 +3,7 @@ "SoilHeightAboveGround": 8, "RecipeIngredients": [ { - "Object": 388, // Wood + "Object": "Wood", "Count": 20 } ], @@ -16,7 +16,7 @@ "SoilHeightAboveGround": 8, "RecipeIngredients": [ { - "Object": 390, // Stone + "Object": "Stone", "Count": 20 } ], @@ -29,7 +29,7 @@ "SoilHeightAboveGround": 8, "RecipeIngredients": [ { - "Object": 330, // Clay + "Object": "Clay", "Count": 10 } ], @@ -42,7 +42,7 @@ "SoilHeightAboveGround": 10, "RecipeIngredients": [ { - "Object": 709, // Hardwood + "Object": "Hardwood", "Count": 5 } ], @@ -55,7 +55,7 @@ "SoilHeightAboveGround": 6, "RecipeIngredients": [ { - "Object": 169, // Driftwood + "Object": "Driftwood", "Count": 3 } ], @@ -68,7 +68,7 @@ "SoilHeightAboveGround": 6, "RecipeIngredients": [ { - "Object": 336, // Gold Bar + "Object": "Gold Bar", "Count": 2 } ], @@ -81,11 +81,11 @@ "SoilHeightAboveGround": 9, "RecipeIngredients": [ { - "Object": 769, // Void Essence + "Object": "Void Essence", "Count": 10 }, { - "Object": 390, // Stone + "Object": "Stone", "Count": 10 } ], @@ -98,11 +98,11 @@ "SoilHeightAboveGround": 6, "RecipeIngredients": [ { - "Object": 771, // Fiber + "Object": "Fiber", "Count": 5 }, { - "Object": 390, // Stone + "Object": "Stone", "Count": 10 } ], @@ -115,11 +115,11 @@ "SoilHeightAboveGround": 8, "RecipeIngredients": [ { - "Object": 382, // Coal + "Object": "Coal", "Count": 2 }, { - "Object": 390, // Stone + "Object": "Stone", "Count": 10 } ], @@ -132,11 +132,11 @@ "SoilHeightAboveGround": 8, "RecipeIngredients": [ { - "Object": 245, // Sugar + "Object": "Sugar", "Count": 2 }, { - "Object": 388, // Wood + "Object": "Wood", "Count": 10 } ], @@ -149,7 +149,7 @@ "SoilHeightAboveGround": 7, "RecipeIngredients": [ { - "Object": 388, // Wood + "Object": "Wood", "Count": 20 } ], @@ -162,11 +162,11 @@ "SoilHeightAboveGround": 6, "RecipeIngredients": [ { - "Object": 881, // Bone Fragment + "Object": "Bone Fragment", "Count": 5 }, { - "Object": 390, // Stone + "Object": "Stone", "Count": 10 } ],