private static bool Object_drawInMenu_Prefix(Object __instance, SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, StackDrawType drawStackNumber, Color color, bool drawShadow) { if (!customChestTypesDict.TryGetValue(__instance.ParentSheetIndex, out var chestInfo)) { return(true); } bool shouldDrawStackNumber = ((drawStackNumber == StackDrawType.Draw && __instance.maximumStackSize() > 1 && __instance.Stack > 1) || drawStackNumber == StackDrawType.Draw_OneInclusive) && scaleSize > 0.3 && __instance.Stack != int.MaxValue; Texture2D texture = chestInfo.texture.First(); float extraSize = Math.Max(texture.Height, texture.Width) / 32f; Rectangle sourceRect = new Rectangle(0, 0, texture.Width, texture.Height); spriteBatch.Draw(texture, location + new Vector2(32f / extraSize, 32f / extraSize), sourceRect, color * transparency, 0f, new Vector2(8f, 16f), 4f * (((double)scaleSize < 0.2) ? scaleSize : (scaleSize / 2f)) / extraSize, SpriteEffects.None, layerDepth); if (shouldDrawStackNumber) { Utility.drawTinyDigits(__instance.stack, spriteBatch, location + new Vector2((float)(64 - Utility.getWidthOfTinyDigitString(__instance.stack, 3f * scaleSize)) + 3f * scaleSize, 64f - 18f * scaleSize + 2f), 3f * scaleSize, 1f, color); } return(false); }
/// <summary>Get all items owned by the player.</summary> /// <remarks> /// Derived from <see cref="Utility.iterateAllItems"/> with some differences: /// * removed items held by other players, items floating on the ground, spawned forage, and output in a non-ready machine (except casks which can be emptied anytime); /// * added hay in silos. /// </remarks> public IEnumerable <Item> GetAllOwnedItems() { List <Item> items = new List <Item>(); // in locations foreach (GameLocation location in CommonHelper.GetLocations()) { // furniture if (location is DecoratableLocation decorableLocation) { foreach (Furniture furniture in decorableLocation.furniture) { items.Add(furniture); if (furniture is StorageFurniture dresser) { items.AddRange(dresser.heldItems); } else { items.Add(furniture.heldObject.Value); } } } // farmhouse fridge if (location is FarmHouse house) { items.AddRange(house.fridge.Value.items); } // character hats foreach (NPC npc in location.characters) { items.Add( (npc as Child)?.hat.Value ?? (npc as Horse)?.hat.Value ); } // building output if (location is BuildableGameLocation buildableLocation) { foreach (var building in buildableLocation.buildings) { if (building is Mill mill) { items.AddRange(mill.output.Value.items); } else if (building is JunimoHut hut) { items.AddRange(hut.output.Value.items); } } } // map objects foreach (SObject item in location.objects.Values) { // chest if (item is Chest chest) { if (chest.playerChest.Value) { items.Add(chest); items.AddRange(chest.items); } } // auto-grabber else if (item.ParentSheetIndex == 165 && item.heldObject.Value is Chest grabberChest) { items.Add(item); items.AddRange(grabberChest.items); } // craftable else if (item.bigCraftable.Value) { items.Add(item); if (item.MinutesUntilReady == 0 || item is Cask) // cask output can be retrieved anytime { items.Add(item.heldObject.Value); } } // anything else else if (!item.IsSpawnedObject) { items.Add(item); items.Add(item.heldObject.Value); } } } // inventory items.AddRange(Game1.player.Items); items.AddRange(new Item[] { Game1.player.shirtItem.Value, Game1.player.pantsItem.Value, Game1.player.boots.Value, Game1.player.hat.Value, Game1.player.leftRing.Value, Game1.player.rightRing.Value }); // hay in silos int hayCount = Game1.getFarm()?.piecesOfHay.Value ?? 0; while (hayCount > 0) { SObject hay = new SObject(178, 1); hay.Stack = Math.Min(hayCount, hay.maximumStackSize()); hayCount -= hay.Stack; items.Add(hay); } return(items.Where(p => p != null)); }
/// <summary>Get all items owned by the player.</summary> /// <remarks> /// This is derived from <see cref="Utility.iterateAllItems"/> with some improvements: /// * removed items held by other players, items floating on the ground, spawned forage, and output in a non-ready machine (except casks which can be emptied anytime); /// * added hay in silos; /// * added tool attachments; /// * added recursive scanning (e.g. inside held chests) to support mods like Item Bag. /// </remarks> public IEnumerable <FoundItem> GetAllOwnedItems() { List <FoundItem> items = new List <FoundItem>(); // in locations foreach (GameLocation location in CommonHelper.GetLocations()) { // furniture if (location is DecoratableLocation decorableLocation) { foreach (Furniture furniture in decorableLocation.furniture) { this.ScanAndTrack(items, furniture, isRootInWorld: true); } } // farmhouse fridge if (location is FarmHouse house) { this.ScanAndTrack(items, house.fridge.Value, includeRoot: false); } // character hats foreach (NPC npc in location.characters) { Hat hat = (npc as Child)?.hat.Value ?? (npc as Horse)?.hat.Value; this.ScanAndTrack(items, hat); } // building output if (location is BuildableGameLocation buildableLocation) { foreach (var building in buildableLocation.buildings) { switch (building) { case Mill mill: this.ScanAndTrack(items, mill.output.Value, includeRoot: false); break; case JunimoHut hut: this.ScanAndTrack(items, hut.output.Value, includeRoot: false); break; } } } // map objects foreach (SObject item in location.objects.Values) { if (item is Chest || !this.IsSpawnedWorldItem(item)) { this.ScanAndTrack(items, item, isRootInWorld: true); } } } // inventory this.ScanAndTrack(items, Game1.player.Items, isInInventory: true); this.ScanAndTrack( items, new Item[] { Game1.player.shirtItem.Value, Game1.player.pantsItem.Value, Game1.player.boots.Value, Game1.player.hat.Value, Game1.player.leftRing.Value, Game1.player.rightRing.Value }, isInInventory: true ); // hay in silos int hayCount = Game1.getFarm()?.piecesOfHay.Value ?? 0; while (hayCount > 0) { SObject hay = new SObject(178, 1); hay.Stack = Math.Min(hayCount, hay.maximumStackSize()); hayCount -= hay.Stack; this.ScanAndTrack(items, hay); } return(items); }
private static bool ObjectDrawInMenuPrefix(SObject __instance, SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, StackDrawType drawStackNumber, Color color, bool drawShadow) { if (__instance is not { ParentSheetIndex : Globals.MEAD_INDEX_I, preservedParentSheetIndex.Value : > 0 } mead) { return(true); // run original logic } var parameters = new object?[] { __instance, null, null, null }; var got = (bool)_GetDrawInfo.Invoke(null, parameters) !; if (!got) { return(true); // run original logic } var spritesheet = (Texture2D)parameters[1] !; var sourceRectangle = (Rectangle)parameters[2] !; if (drawShadow) { var shadowTexture = Game1.shadowTexture; spriteBatch.Draw( texture: shadowTexture, position: location + new Vector2(32f, 48f), sourceRectangle: shadowTexture.Bounds, color: color * 0.5f, rotation: 0f, origin: new(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), scale: 3f, effects: SpriteEffects.None, layerDepth: layerDepth - 0.0001f ); } spriteBatch.Draw( texture: spritesheet, position: location + new Vector2( (int)(32f * scaleSize), (int)(32f * scaleSize) ), sourceRectangle: sourceRectangle, color: color * transparency, rotation: 0f, origin: new Vector2(8f, 8f) * scaleSize, scale: 4f * scaleSize, effects: SpriteEffects.None, layerDepth: layerDepth ); var shouldDrawStackNumber = (drawStackNumber == StackDrawType.Draw && __instance.maximumStackSize() > 1 && __instance.Stack > 1 || drawStackNumber == StackDrawType.Draw_OneInclusive) && scaleSize > 0.3 && __instance.Stack != int.MaxValue; if (shouldDrawStackNumber) { Utility.drawTinyDigits( toDraw: __instance.Stack, spriteBatch, position: location + new Vector2( 64 - Utility.getWidthOfTinyDigitString(__instance.Stack, 3f * scaleSize) + 3f * scaleSize, 64f - 18f * scaleSize + 1f ), scale: 3f * scaleSize, layerDepth: 1f, color ); } if (drawStackNumber != StackDrawType.Hide && __instance.Quality > 0) { var num = __instance.Quality < 4 ? 0f : ((float)Math.Cos(Game1.currentGameTime.TotalGameTime.Milliseconds * Math.PI / 512.0) + 1f) * 0.05f; spriteBatch.Draw( texture: Game1.mouseCursors, position: location + new Vector2(12f, 52f + num), sourceRectangle: __instance.Quality < 4 ? new(338 + (__instance.Quality - 1) * 8, 400, 8, 8) : new(346, 392, 8, 8), color: color * transparency, rotation: 0f, origin: new(4f, 4f), scale: 3f * scaleSize * (1f + num), effects: SpriteEffects.None, layerDepth: layerDepth ); } return(false); // don't run original logic }
/// <summary>Get all items owned by the player.</summary> /// <remarks> /// This is derived from <see cref="Utility.iterateAllItems"/> with some improvements: /// * removed items held by other players, items floating on the ground, spawned forage, and output in a non-ready machine (except casks which can be emptied anytime); /// * added hay in silos; /// * added tool attachments; /// * added recursive scanning (e.g. inside held chests) to support mods like Item Bag. /// </remarks> public IEnumerable <FoundItem> GetAllOwnedItems() { List <FoundItem> items = new List <FoundItem>(); ISet <Item> itemsSeen = new HashSet <Item>(new ObjectReferenceComparer <Item>()); // in locations foreach (GameLocation location in CommonHelper.GetLocations()) { // furniture foreach (Furniture furniture in location.furniture) { this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: furniture, parent: location, isRootInWorld: true); } // farmhouse fridge Chest fridge = location switch { FarmHouse house => house.fridge.Value, IslandFarmHouse house => house.fridge.Value, _ => null }; this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: fridge, parent: location, includeRoot: false); // character hats foreach (NPC npc in location.characters) { Hat hat = (npc as Child)?.hat.Value ?? (npc as Horse)?.hat.Value; this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: hat, parent: npc); } // building output if (location is BuildableGameLocation buildableLocation) { foreach (var building in buildableLocation.buildings) { switch (building) { case Mill mill: this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: mill.output.Value, parent: mill, includeRoot: false); break; case JunimoHut hut: this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: hut.output.Value, parent: hut, includeRoot: false); break; } } } // map objects foreach (SObject item in location.objects.Values) { if (item is Chest || !this.IsSpawnedWorldItem(item)) { this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: item, parent: location, isRootInWorld: true); } } } // inventory this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, roots: Game1.player.Items, parent: Game1.player, isInInventory: true); this.ScanAndTrack( tracked: items, itemsSeen: itemsSeen, roots: new Item[] { Game1.player.shirtItem.Value, Game1.player.pantsItem.Value, Game1.player.boots.Value, Game1.player.hat.Value, Game1.player.leftRing.Value, Game1.player.rightRing.Value }, parent: Game1.player, isInInventory: true ); // hay in silos Farm farm = Game1.getFarm(); int hayCount = farm?.piecesOfHay.Value ?? 0; while (hayCount > 0) { SObject hay = new SObject(178, 1); hay.Stack = Math.Min(hayCount, hay.maximumStackSize()); hayCount -= hay.Stack; this.ScanAndTrack(tracked: items, itemsSeen: itemsSeen, root: hay, parent: farm); } return(items); }
/// <summary>Draw the correct texture based on <see cref="SObject.preservedParentSheetIndex"/> or <see cref="SObject.Name"/>.</summary> private static bool Prefix(SObject __instance, SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, Color color, StackDrawType drawStackNumber, bool drawShadow) { if (!ModEntry.Instance.GetDrawInfo(__instance, out Texture2D spriteSheet, out Rectangle position, out Rectangle iconPosition)) { return(true); } if (__instance.IsRecipe) { transparency = 0.5f; scaleSize *= 0.75f; } bool flag = (drawStackNumber == StackDrawType.Draw && __instance.maximumStackSize() > 1 && __instance.Stack > 1 || drawStackNumber == StackDrawType.Draw_OneInclusive) && (double)scaleSize > 0.3 && __instance.Stack != int.MaxValue; if (__instance.IsRecipe) { flag = false; } if (__instance.bigCraftable.Value) { Microsoft.Xna.Framework.Rectangle rectForBigCraftable = SObject.getSourceRectForBigCraftable(__instance.ParentSheetIndex); spriteBatch.Draw(Game1.bigCraftableSpriteSheet, location + new Vector2(32f, 32f), new Microsoft.Xna.Framework.Rectangle?(rectForBigCraftable), color * transparency, 0.0f, new Vector2(8f, 16f), (float)(4.0 * ((double)scaleSize < 0.2 ? (double)scaleSize : (double)scaleSize / 2.0)), SpriteEffects.None, layerDepth); if (flag) { Utility.drawTinyDigits(__instance.Stack, spriteBatch, location + new Vector2((float)(64 - Utility.getWidthOfTinyDigitString(__instance.Stack, 3f * scaleSize)) + 3f * scaleSize, (float)(64.0 - 18.0 * (double)scaleSize + 2.0)), 3f * scaleSize, 1f, color); } } else { if (__instance.ParentSheetIndex != 590 & drawShadow) { spriteBatch.Draw(Game1.shadowTexture, location + new Vector2(32f, 48f), new Microsoft.Xna.Framework.Rectangle?(Game1.shadowTexture.Bounds), color * 0.5f, 0.0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), 3f, SpriteEffects.None, layerDepth - 0.0001f); } // modified spriteBatch.Draw(spriteSheet, location + new Vector2((float)(int)(32.0 * (double)scaleSize), (float)(int)(32.0 * (double)scaleSize)), new Microsoft.Xna.Framework.Rectangle?(position), color * transparency, 0.0f, new Vector2(8f, 8f) * scaleSize, 4f * scaleSize, SpriteEffects.None, layerDepth); if (flag) { Utility.drawTinyDigits(__instance.Stack, spriteBatch, location + new Vector2((float)(64 - Utility.getWidthOfTinyDigitString(__instance.Stack, 3f * scaleSize)) + 3f * scaleSize, (float)(64.0 - 18.0 * (double)scaleSize + 1.0)), 3f * scaleSize, 1f, color); } // New if (transparency >= 1 && iconPosition != Rectangle.Empty) { spriteBatch.Draw(Game1.objectSpriteSheet, location + new Vector2(Game1.tileSize / 6 * scaleSize, Game1.tileSize / 6 * scaleSize), new Microsoft.Xna.Framework.Rectangle?(iconPosition), Color.White * transparency, 0.0f, new Vector2(4f, 4f), (3f / 2f) * scaleSize, SpriteEffects.None, layerDepth); } if (drawStackNumber != StackDrawType.Hide && __instance.Quality > 0) { float num = __instance.Quality < 4 ? 0.0f : (float)((Math.Cos((double)Game1.currentGameTime.TotalGameTime.Milliseconds * Math.PI / 512.0) + 1.0) * 0.0500000007450581); spriteBatch.Draw(Game1.mouseCursors, location + new Vector2(12f, 52f + num), new Microsoft.Xna.Framework.Rectangle?(__instance.Quality < 4 ? new Microsoft.Xna.Framework.Rectangle(338 + (__instance.Quality - 1) * 8, 400, 8, 8) : new Microsoft.Xna.Framework.Rectangle(346, 392, 8, 8)), color * transparency, 0.0f, new Vector2(4f, 4f), (float)(3.0 * (double)scaleSize * (1.0 + (double)num)), SpriteEffects.None, layerDepth); } if (__instance.Category == -22 && __instance.uses.Value > 0) { float power = ((float)(FishingRod.maxTackleUses - __instance.uses.Value) + 0.0f) / (float)FishingRod.maxTackleUses; spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle((int)location.X, (int)((double)location.Y + 56.0 * (double)scaleSize), (int)(64.0 * (double)scaleSize * (double)power), (int)(8.0 * (double)scaleSize)), Utility.getRedToGreenLerpColor(power)); } } if (__instance.IsRecipe) { spriteBatch.Draw(Game1.objectSpriteSheet, location + new Vector2(16f, 16f), new Microsoft.Xna.Framework.Rectangle?(Game1.getSourceRectForStandardTileSheet(Game1.objectSpriteSheet, 451, 16, 16)), color, 0.0f, Vector2.Zero, 3f, SpriteEffects.None, layerDepth + 0.0001f); } return(false); }
/// <summary> /// Patch for drawing a custom sprite (if available) for the object in the menu. /// </summary> /// <returns>If original method should be executed.</returns> /// <param name="__instance">The object to be drawn.</param> /// <param name="spriteBatch">Sprite batch to draw onto.</param> /// <param name="location">Where to draw on the sprite batch.</param> /// <param name="scaleSize">Drawing scale.</param> /// <param name="transparency">Transparency.</param> /// <param name="layerDepth">Layer depth.</param> /// <param name="drawStackNumber">If set to <c>true</c> draw stack number.</param> /// <param name="color">Color.</param> /// <param name="drawShadow">If set to <c>true</c> draw shadow.</param> public static bool Prefix( SObject __instance, SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, bool drawStackNumber, Color color, bool drawShadow ) { if (!SpriteLoader.TryLoadSprite(__instance, out Texture2D texture, out Rectangle sourceRect)) { return(true); } if (drawShadow) { Texture2D shadowTexture = Game1.shadowTexture; spriteBatch.Draw( texture: shadowTexture, position: location + new Vector2(32f, 48f), sourceRectangle: shadowTexture.Bounds, color: color * 0.5f, rotation: 0f, origin: new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), scale: 3f, effects: SpriteEffects.None, layerDepth: layerDepth - 0.0001f ); } spriteBatch.Draw( texture: texture, position: location + new Vector2( (int)(32f * scaleSize), (int)(32f * scaleSize) ), sourceRectangle: sourceRect, color: color * transparency, rotation: 0f, origin: new Vector2(8f, 8f) * scaleSize, scale: 4f * scaleSize, effects: SpriteEffects.None, layerDepth: layerDepth ); if (drawStackNumber && __instance.maximumStackSize() > 1 && scaleSize > 0.3 && __instance.Stack != 2147483647 && __instance.Stack > 1) { StardewValley.Utility.drawTinyDigits( toDraw: __instance.Stack, spriteBatch, position: location + new Vector2( 64 - StardewValley.Utility.getWidthOfTinyDigitString(__instance.Stack, 3f * scaleSize) + 3f * scaleSize, 64f - 18f * scaleSize + 2f ), scale: 3f * scaleSize, layerDepth: 1f, color ); } if (drawStackNumber && __instance.Quality > 0) { float num = (__instance.Quality < 4) ? 0f : (((float)Math.Cos(Game1.currentGameTime.TotalGameTime.Milliseconds * Math.PI / 512.0) + 1f) * 0.05f); spriteBatch.Draw( texture: Game1.mouseCursors, position: location + new Vector2(12f, 52f + num), sourceRectangle: (__instance.Quality < 4) ? new Rectangle(338 + (__instance.Quality - 1) * 8, 400, 8, 8) : new Rectangle(346, 392, 8, 8), color: color * transparency, rotation: 0f, origin: new Vector2(4f, 4f), scale: 3f * scaleSize * (1f + num), effects: SpriteEffects.None, layerDepth: layerDepth ); } return(false); }
internal static void OnGameLaunched() { IModHelper Helper = ItemBagsMod.ModInstance.Helper; #if NEVER //DEBUG try { // Retrieve the item Ids of various new items added by update 1.5 List <string> NewItemNames = new List <string>() { "Bug Steak", "Banana Pudding", "Ginger", "Ginger Ale", "Ginger Beer", "Mango Sticky Rice", "Piña Colada", "Poi", "Taro Root", "Tropical Curry", "Squid Ink Ravioli", "Deluxe Fertilizer", "Deluxe Retaining Soil", "Hyper Speed-Gro", "Bone Fragment", "Fossilized Skull", "Fossilized Spine", "Fossilized Tail", "Fossilized Leg", "Fossilized Ribs", "Snake Skull", "Snake Vertebrae", "Cinder Shard", "Dragon Tooth", "Tiger Slime Egg", "Fairy Dust", "Golden Walnut", "Magma Cap", "Monster Musk", "Mummified Bat", "Mummified Frog", "Ostrich Egg", "Qi Gem", "Qi Seasoning", "Radioactive Ore", "Radioactive Bar", "Taro Tuber", "Mushroom Tree Seed", "Magic Bait", "Stingray", "Lionfish", "Blue Discus", "Glacierfish Jr.", "Legend II", "Ms. Angler", "Radioactive Carp", "Son of Crimsonfish", "Cookout Kit", "Coffee Maker", "Mahogany Seed", "Heavy Tapper", "Mango Sapling", "Mango", "Banana Sapling", "Banana", "Farm Computer", "Mini-Shipping Bin", "Warp Totem: Island", "Bone Mill", "Mini-Obelisk", "Ostrich Incubator", "Geode Crusher", "Pineapple Seeds", "Pineapple", "Dark Sign", "Deconstructor", "Hopper", "Solar Panel", "Fiber Seeds" }; //Game1.objectInformation.GroupBy(x => x.Value).Select(x => x.First()).OrderBy(x => x.Value).ToDictionary(x => x.Value, x => x.Key); Dictionary <string, int> AllItems = new Dictionary <string, int>(); foreach (var item in Game1.objectInformation) { AllItems[item.Value.Split('/').First()] = item.Key; } foreach (var item in Game1.bigCraftablesInformation) { AllItems[item.Value.Split('/').First()] = item.Key; } AllItems = AllItems.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); List <string> NotFound = new List <string>(); List <string> Result = new List <string>(); Dictionary <string, int> NewItems = new Dictionary <string, int>(); foreach (string Name in NewItemNames.OrderBy(x => x)) { if (!AllItems.TryGetValue(Name, out int Id)) { NotFound.Add(Name); } else { NewItems.Add(Name, Id); Object Instance = new Object(Id, 1); bool HasQualities = CategoriesWithQualities.Contains(Instance.Category); bool IsStackable = Instance.maximumStackSize() > 1; string Category = Instance.getCategoryName(); if (string.IsNullOrEmpty(Category)) { Category = Instance.Category.ToString(); } Result.Add(string.Format("{0} ({1}, {2}, {3}) (Quality={4})", Name, Id, Category, ItemBag.GetSingleItemPrice(Instance), HasQualities.ToString())); } } string NewItemInfo = string.Join("\n", Result); } catch (Exception) { } #endif // Load modded items from JsonAssets the moment it finishes registering items if (Helper.ModRegistry.IsLoaded(ItemBagsMod.JAUniqueId)) { IJsonAssetsAPI API = Helper.ModRegistry.GetApi <IJsonAssetsAPI>(ItemBagsMod.JAUniqueId); if (API != null) { API.IdsFixed += (sender, e) => { OnJsonAssetsIdsFixed(API, ItemBagsMod.BagConfig); }; } } }