private void UpdateQuantities() { // Initialize all quantities back to zero foreach (Object Placeholder in Placeholders.SelectMany(x => x.Value.Values)) { Placeholder.Stack = 0; } // Set quantities of the placeholder items to match the corresponding amount of the item currently stored in the bag foreach (Object Item in Bag.Contents) { if (Placeholders.TryGetValue(Item.ParentSheetIndex, out Dictionary <ObjectQuality, Object> Group)) { ObjectQuality Quality = (ObjectQuality)Item.Quality; if (Group.TryGetValue(Quality, out Object Placeholder)) { ItemBag.ForceSetQuantity(Placeholder, Item.Stack); if (Placeholder.Price != Item.Price) { #if DEBUG string WarningMsg = string.Format("Warning - GroupedLayout placeholder item '{0}' does not have a matching price to the corresponding item in the bag." + " Placeholder.Price={1}, BagItem.Price={2}", Placeholder.DisplayName, Placeholder.Price, Item.Price); ItemBagsMod.ModInstance.Monitor.Log(WarningMsg, LogLevel.Warn); #endif Placeholder.Price = Item.Price; } } } } }
public Object ToObject() { if (IsBigCraftable) { Object Item = new Object(Vector2.Zero, Id, false) { Price = this.Price }; // It seems like some modded items don't have their price set properly if not explicitly specified ItemBag.ForceSetQuantity(Item, this.Quantity); return(Item); } else { Object Item = new Object(Id, Quantity, false, Price <= 0 ? -1 : Price, Quality); if (!string.IsNullOrEmpty(Name)) { Item.Name = Name; } if (this.HoneyType != null) { Item.honeyType.Value = (Object.HoneyType) this.HoneyType.Value; } if (this.PreserveType != null) { Item.preserve.Value = (Object.PreserveType) this.PreserveType.Value; } if (this.PreservedId != int.MinValue) { Item.preservedParentSheetIndex.Value = this.PreservedId; } #if DEBUG if (Item.Price <= 0 && Item.Price != this.Price) { string WarningMsg = string.Format("Warning - Item '{0}' did not have its price set to a non-zero value after being created. Expected Price = {1}, Actual Price = {2}", Item.DisplayName, this.Price, Item.Price); ItemBagsMod.ModInstance.Monitor.Log(WarningMsg, StardewModdingAPI.LogLevel.Warn); } #endif // Sanity check in case Stack > 999 and StardewValley is updated to set the Object.Stack in its constructor instead of Object.stack // (Object.Stack has a setter that restricts maximum value to the range 0-999, while Object.stack (the backing Net field) does not) if (Item.Stack != Quantity) { ItemBag.ForceSetQuantity(Item, Quantity); } return(Item); } }
private void UpdateQuantities() { // Initialize all quantities back to zero foreach (Object Placeholder in PlaceholderItems) { Placeholder.Stack = 0; } // Set quantities of the placeholder items to match the corresponding amount of the item currently stored in the bag foreach (Object Item in Bag.Contents) { Object Placeholder = PlaceholderItems.FirstOrDefault(x => ItemBag.AreItemsEquivalent(x, Item, false, true)); if (Placeholder != null) { ItemBag.ForceSetQuantity(Placeholder, Item.Stack); } } }
private static void OnCraftingPageDeactivated() { try { // If the player had the CraftingPage of the GameMenu opened, they may have crafted items using materials that were inside their bags, // so we must Resynchronize the bag contents in multiplayer if (BagsInUse != null && BagsInUse.Any()) { // Recombine item Stacks that were split to avoid having > 999 if (SplitStacks != null && SplitStacks.Any()) { foreach (KeyValuePair <Object, List <Object> > KVP in SplitStacks) { int NewQuantity = KVP.Value.Sum(x => x.Stack); ItemBag.ForceSetQuantity(KVP.Key, NewQuantity); } } foreach (ItemBag Bag in BagsInUse) { if (Bag is OmniBag OB) { foreach (ItemBag NestedBag in OB.NestedBags) { NestedBag.Contents.RemoveAll(x => x == null || x.Stack <= 0); } } else { Bag.Contents.RemoveAll(x => x == null || x.Stack <= 0); } Bag.Resync(); } } } finally { BagsInUse = null; SplitStacks = null; } }
/// <summary>Initializes extra data for the Crafting Page so it can search for and use materials within bags in your inventory.</summary> private static void OnCraftingPageActivated(CraftingPage CraftingMenu) { // Allow the CraftingPage to search for and use items inside of bags bool AllowUsingBundleBagItemsForCrafting = false; List <ItemBag> BagsInInventory = Game1.player.Items.Where(x => x != null && x is ItemBag).Cast <ItemBag>().ToList(); if (BagsInInventory.Any()) { BagsInUse = new HashSet <ItemBag>(BagsInInventory); // Get the "_materialContainers" protected field that defines additional item containers to search for when using up materials during crafting IReflectedField <List <Chest> > ReflectionResult = Helper.Reflection.GetField <List <Chest> >(CraftingMenu, "_materialContainers", true); List <Chest> MaterialContainers = ReflectionResult.GetValue(); if (MaterialContainers == null) { MaterialContainers = new List <Chest>(); ReflectionResult.SetValue(MaterialContainers); } // Create a temporary chest from the items of each bag, and add the chest to _materialContainers foreach (ItemBag IB in BagsInInventory.Where(x => AllowUsingBundleBagItemsForCrafting || !(x is BundleBag))) { // Note that if the item inside the bag has Stack > 999, it must be split up into chunks with Stacks <= 999 // Because the Game truncates the actual stack down to 999 anytime it modifies a stack value if (IB is OmniBag OB) { foreach (ItemBag NestedBag in OB.NestedBags.Where(x => AllowUsingBundleBagItemsForCrafting || !(x is BundleBag))) { List <Item> TemporaryChestContents = new List <Item>(); foreach (Object Item in NestedBag.Contents) { if (Item.Stack > 999) { if (SplitStacks == null) { SplitStacks = new Dictionary <Object, List <Object> >(); } List <Object> Chunks = new List <Object>(); int TotalStack = Item.Stack; int DistributedAmt = 0; while (DistributedAmt < TotalStack) { int CurrentStackAmt = Math.Min(999, TotalStack - DistributedAmt); Object Chunk = ItemBag.CreateCopy(Item); ItemBag.ForceSetQuantity(Chunk, CurrentStackAmt); DistributedAmt += CurrentStackAmt; Chunks.Add(Chunk); } SplitStacks.Add(Item, Chunks); TemporaryChestContents.AddRange(Chunks); } else { TemporaryChestContents.Add(Item); } } Chest TempChest = new Chest(0, TemporaryChestContents, Vector2.Zero, false, 0); MaterialContainers.Add(TempChest); } } else { List <Item> TemporaryChestContents = new List <Item>(); foreach (Object Item in IB.Contents) { if (Item.Stack > 999) { if (SplitStacks == null) { SplitStacks = new Dictionary <Object, List <Object> >(); } List <Object> Chunks = new List <Object>(); int TotalStack = Item.Stack; int DistributedAmt = 0; while (DistributedAmt < TotalStack) { int CurrentStackAmt = Math.Min(999, TotalStack - DistributedAmt); Object Chunk = ItemBag.CreateCopy(Item); ItemBag.ForceSetQuantity(Chunk, CurrentStackAmt); DistributedAmt += CurrentStackAmt; Chunks.Add(Chunk); } SplitStacks.Add(Item, Chunks); TemporaryChestContents.AddRange(Chunks); } else { TemporaryChestContents.Add(Item); } } Chest TempChest = new Chest(0, TemporaryChestContents, Vector2.Zero, false, 0); MaterialContainers.Add(TempChest); } } } }
public void InitializePlaceholders() { List <Object> Temp = new List <Object>(); IEnumerable <Object> SortedContents; switch (Rucksack.SortProperty) { case SortingProperty.Time: SortedContents = Rucksack.Contents; break; case SortingProperty.Name: SortedContents = Rucksack.Contents.OrderBy(x => x.DisplayName); break; case SortingProperty.Id: SortedContents = Rucksack.Contents.OrderBy(x => x.bigCraftable.Value).ThenBy(x => x.ParentSheetIndex); break; case SortingProperty.Category: //SortedContents = Rucksack.Contents.OrderBy(x => x.getCategorySortValue()); SortedContents = Rucksack.Contents.OrderBy(x => x.getCategoryName()); break; case SortingProperty.Quantity: SortedContents = Rucksack.Contents.OrderBy(x => x.Stack); break; case SortingProperty.SingleValue: SortedContents = Rucksack.Contents.OrderBy(x => ItemBag.GetSingleItemPrice(x)); break; case SortingProperty.StackValue: SortedContents = Rucksack.Contents.OrderBy(x => ItemBag.GetSingleItemPrice(x) * x.Stack); break; case SortingProperty.Similarity: //Possible TODO: Maybe SortingProperty.Similarity shouldn't exist - maybe it should just be a "bool GroupBeforeSorting" //that only groups by ItemId (and maybe also sorts by Quality after). Then the grouping can be applied to any of the other sorting properties. //So it could just be a togglebutton to turn on/off like the Autofill Toggle SortedContents = Rucksack.Contents .OrderBy(Item => Item.getCategoryName()).GroupBy(Item => Item.getCategoryName()) // First sort and group by CategoryName .SelectMany( CategoryGroup => CategoryGroup.GroupBy(Item => Item.ParentSheetIndex) // Then Group by item Id .SelectMany(IdGroup => IdGroup.OrderBy(y => y.Quality)) // Then sort by Quality ); break; default: throw new NotImplementedException(string.Format("Unexpected SortingProperty: {0}", Rucksack.SortProperty.ToString())); } if (Rucksack.SortOrder == SortingOrder.Descending) { SortedContents = SortedContents.Reverse(); } //Possible TODO Add filtering? //For EX: if you only wanted to show Fish, //SortedContents = SortedContents.Where(x => x.Category == <WhateverTheIdIsForFishCategory>); TempVisualFeedback = new Dictionary <Object, DateTime>(); foreach (Object Item in SortedContents) { bool WasRecentlyModified = Bag.RecentlyModified.TryGetValue(Item, out DateTime ModifiedTime); int NumSlots = (Item.Stack - 1) / Rucksack.MaxStackSize + 1; int RemainingQty = Item.Stack; for (int i = 0; i < NumSlots; i++) { Object Copy = ItemBag.CreateCopy(Item); ItemBag.ForceSetQuantity(Copy, Math.Min(RemainingQty, Rucksack.MaxStackSize)); Temp.Add(Copy); RemainingQty -= Copy.Stack; if (WasRecentlyModified) { TempVisualFeedback.Add(Copy, ModifiedTime); } } } this.PlaceholderItems = Temp.AsReadOnly(); }