This article describes a new feature added to ItemBags version 3.1.0. Normally, to create a modded bag, you must explicitly specify each item that the bag can store. ItemFilters allow you to define the bag's items in a more convenient way by using filters to match the items against. Any items that pass the filters will be storeable in the modded bag.
You must install ItemBags version 3.1.0 or later to use these features
Basic Usage
To use ItemFilters, add the ItemFilters json property to the modded bag's .json file. This property is an array of strings, where each entry describes one or more filters:
{
...
"ItemFilters": [
"CategoryId:-4"
]
...
}
This would match all items belonging to Category=-4 (fish category). There are several filter types you can use, such as "FromMod" to match items belonging to a particular ModId.
Each filter is defined in the format: "{FilterType}:{FilterValue}". Some FilterTypes do not require a value, such as the "IsPendingDonation" filter, which matches items that haven't been donated to the museum yet:
{
...
"ItemFilters": [
"IsPendingDonation"
]
...
}
OR-ing and AND-ing filters together
{
...
"ItemFilters": [
"FromMod:Rafseazz.RSVCP|FromMod:FlashShifter.StardewValleyExpandedCP"
]
...
}
This would match all items that were added by either Rideside Village (Rafseazz.RSVCP) OR Stardew Valley Expanded (FlashShifter.StardewValleyExpandedCP).
If you specify multiple entries in the array, they will be logically AND-ed together, so the resulting combination filter will match items that pass ALL of the filters that were AND-ed together.
{
...
"ItemFilters": [
"FromMod:Rafseazz.RSVCP",
"HasBuffs|CategoryId:-4"
]
...
}
This would match items from Rideside Village that either have buffs OR are fish items. (So the logic is essentially: "(Is from RSV) AND (Has Buffs OR Category=-4)"). Notice that we mix and matched, by using a filter composed via logical OR-ing, and a filter composed via logical AND-ing.
Negation Operator
{
...
"ItemFilters": [
"!FromMod:Rafseazz.RSVCP",
"CategoryId:-4"
]
...
}
This would match all fish items (Category=-4) that are NOT from RSV mod.
Sorting
By default, item's in the bag's menu will appear in the order they were found within the game's item database. If you had a bag that was matching any fruits or vegetable ("CategoryId:-79|CategoryId:-75"), which would be shown first in the topleft of the bag menu? The fruits? Or the vegetables? I have no idea how the game's item database is ordered so you'd just have to test it and see the result.
Luckily if you want more control over the ordering, you can add the ItemFiltersSorting property to the modded bag's .json file.
The value should be a comma-separated list of the properties to sort by, where the property can be any of the following:
CategoryId
Price
ObjectType
Edibility (an integer value that determines the item's health/energy restoration)
InternalName (the un-localized English item name)
DisplayName (The localized item name)
LocalId (The unualified Id)
QualifiedId (The fully-qualified Id which includes the item type such as "(O)" or "(BC)")
{
...
"ItemFiltersSorting": "DisplayName"
...
}
"DisplayName" would sort the results by their displayname. "CategoryId,DisplayName" would first sort the items by their category, then by their displayname.
You can optionally suffix each value with a hyphen followed by either "Ascending" or "Descending" to control the sorting direction:
"CategoryId-Descending,DisplayName-Ascending"
If no direction is specified, it will use Ascending order.
Note: CategoryIds are negative values, so Ascending order would put things like fish (Id=-4) before gem (Id=-2)
FilterTypes
- BagSize
Expected value: One of the following bag sizes: "Small", "Medium", "Large", "Giant", "Massive", optionally prefixed with one of the following modifiers: "<", ">", "<=", ">=", "="
Example: "BagSize:>=Large" (This would match any size >= Large, which would be Large OR Giant OR Massive)
Remarks: This filter can be useful if you want larger bag sizes to be capable of storing more items. For example,
{
...
"ItemFilters": [
"CategoryId:-5|CategoryId:-6",
"CategoryId:-5|!BagSize:Small"
]
...
}
This would allow the bag to store eggs (id=-5) and milks (id=-6), BUT milks cannot be stored in the Smallest bag size. (So Small bags only store eggs, and every other size stores eggs+milks)
- IsVanillaItem
Expected value: None
Example: "IsVanillaItem"
Remarks: To detect if an item is a vanilla item or not, ItemBags simply looks for an underscore in the item's Id. This is because mods usually name their item Ids in this format: "{ModId}_{ItemId}". If a mod doesn't use this naming convention, ItemBags could incorrectly detect it's items as if they were vanilla items.
- FromMod
Expected value: The mod's UniqueId, found in the mod's manifest.json file.
Example: "FromMod:FlashShifter.StardewValleyExpandedCP"
Remarks: To detect if an item is from a particular mod, ItemBags checks to see if the item's Id starts with the mod's UniqueId. This is because mods usually name their item Ids in this format: "{ModId}_{ItemId}". If a mod doesn't use this naming convention, ItemBags won't be able to know that the item came from that mod. If a mod has multiple components, use the UniqueId of whichever component added the items to the game (usually the CP - Content Patcher portion of the mod).
- HasMod
Expected value: The mod's UniqueId, found in the mod's manifest.json file. Can optionally be suffixed with "-{MinimumVersion}"
Example: "Rafseazz.RSVCP-2.5.17" (Checks if RSV version 2.5.17 or later is installed)
- CategoryId
Expected value: An integer representing the desired category Id. Category Ids can be found here. You can also specify several categories, separated by commas.
Examples: "CategoryId:-5", "CategoryId:-5,-6"
Remarks: Not all items have a valid category. I think the game just uses a value of zero for uncategorized items?
- IsBigCraftable
Expected value: None
Example: "IsBigCraftable"
Remarks: Bags are only capable of storing BigCraftables or standard Objects. They can't store things like tools or equippables (hats, rings, boots, weapons etc). So if you negate this filter, it will only match standard Objects.
- Quality
Expected value: A valid quality, either defined by it's name ("normal", "silver", "gold", "iridium"), or defined by its internal integer value (0, 1, 2, or 4) (Yes, quality value of 4 means iridium. NOT 3. Don't ask me why)
Example: "Quality:Iridium"
Remarks: When processing an item, ItemBags will attempt to 'guess' if the item is available in multiple different qualities, or if the item can only be regular quality. It makes this guess based on the item's category. Categories like fish, eggs, milks, fruits, vegetables, flowers etc, are assumed to be available in all 4 qualities. While other items such as gems or minerals are assumed to only be available in 'normal' quality. If you use this filter to attempt to match non-regular qualities of an item that ItemBags assumes aren't available in non-regular qualities, then the item would not pass the filter. You can override which categories support quality-levels using the CategoryQualities json property (more details later in this article, in the Quality Levels section)
- HasContextTag
Expected value: The name of the context tag
Example: "HasContextTag:color_iridium"
Remarks: The wiki only lists some of the context tags, and mods can add their own unique context tags to their items as well. So this filter is more useful than it may seem at first glance. You can use the console command debug listtags to see the context tags on whatever item you're currently holding.
- HasBuffs
Expected value: None
Example: "HasBuffs"
Remarks: Stamina/Health restorative effects are not a buff, so this filter doesn't necessarily match all food items.
- IsDonatable
Expected value: None
Example: "IsDonatable"
Remarks: This includes items that can be donated to the museum, or to the fossil island office on Ginger Island.
- IsPendingDonation
Expected value: None
Example: "IsPendingDonation"
Remarks: ItemBags only calculates this filter's matches when a save file is first loaded after launching the game. It does not dynamically update. So it won't reflect changes if you just donated an item, or if you load a different save file that has donated different items. In other words, re-launch the game to let ItemBags fully refresh the valid items.
- QualifiedId filters: QualifiedId, QualifiedIdPrefix, QualifiedIdSuffix, QualifiedIdContains
Expected value: The QualifiedId filter matches against an exactly-specified value. The Prefix filter looks for Ids that start with the given value. The Suffix filter looks for Ids that end with the given value. The Contains filter looks for Ids that contain the given text.
Example: "QualifiedIdContains:Fish"
Remarks: The values are case-sensitive
- LocalId filters: LocalId, LocalIdPrefix, LocalIdSuffix, LocalIdContains
Expected value: The LocalId filter matches against an exactly-specified value. The Prefix filter looks for Ids that start with the given value. The Suffix filter looks for Ids that end with the given value. The Contains filter looks for Ids that contain the given text.
Example: "LocalIdContains:Fish"
Remarks: The values are case-sensitive
- Name filters: Name, NamePrefix, NameSuffix, NameContains
Expected value: The Name filter matches against an exactly-specified value. The Prefix filter looks for names that start with the given value. The Suffix filter looks for names that end with the given value. The Contains filter looks for names that contain the given text.
Example: "NameContains:Soup"
Remarks: The values are case-sensitive. The values are compared against the item's internal name, NOT the display name. This means that it doesn't use the localized item name - all names are their default English names.
- DisplayName filters: DisplayName, DisplayNamePrefix, DisplayNameSuffix, DisplayNameContains
Expected value: The DisplayName filter matches against an exactly-specified value. The Prefix filter looks for DisplayNames that start with the given value. The Suffix filter looks for DisplayNames that end with the given value. The Contains filter looks for DisplayNames that contain the given text.
Example: "DisplayNameContains:Soup"
Remarks: The values are case-sensitive. The values are compared against the item's DisplayName instead of its InternalName. This means that it uses the item name in the current language rather than always using the English name.
- Regex filters: InternalNameRegex, DisplayNameRegex, LocalIdRegex, QualifiedIdRegex
Expected value: A regex pattern string
Example: "DisplayNameRegex:^[A-C]"
Remarks:
{
...
"ItemFilters": [
"CategoryId:-4",
"DisplayNameRegex:^[A-D]"
]
...
}
The regex pattern ^[A-D] matches text that begins with the letters A through D, so any display name starting with 'A', 'B', 'C', or 'D'
{
...
"ItemFilters": [
"CategoryId:-4",
"LocalIdRegex:^\\d{3}$"
]
...
}
The regex pattern ^\\d{3}$ matches text that contains exactly 3 digits (The \d matches any digit, 0-9, and the {3} modifier applies it 3 times in a row) (Note: We had to escape the backslash so that the json would parse. ^\d{3}$ would not be valid JSON). So this set of filters would match all fish (category=-4), that were added to the game before the 1.6 game update (because prior to 1.6, ItemIds were always numerical values, but after that they were switched to text values and newer fish use the fish's name in their Id)
Note: Regexes often use the vertical pipe character (|) but that character is currently reserved for logically OR-ing multiple filters together. If you need to use the '|' character in your regex, you will have to override the filter delimiter by adding a property to the json file called ItemFiltersDelimiter.
{
...
"ItemFilters": [
"CategoryId:-4",
"DisplayNameRegex:^(A|B|C)||DisplayNamePrefix:H"
],
"ItemFiltersDelimiter": "||",
...
}
In this example, the delimiter is overriden to "||" instead of just "|". So now the regex pattern (^(A|B|C|D)) can use single '|' characters without them being mistaken for a filter delimiter.
Limit / Offset
The Limit setting determines the maximum number of items that a filter can match before it stops matching anything.
The Offset setting determines how many matched items a filter will skip before its matches are added to the result set.
You can apply a Limit and/or Offset by adding the ItemFiltersLimit property or ItemFiltersOffset property to the modded bag's .json file.
{
...
"ItemFilters": [
"CategoryId:-2"
],
"ItemFiltersLimit": 4,
"ItemFiltersOffset": 0,
...
}
This would normally match all gems (category=-2), but since the limit is set to 4, it only matches the first 4 gems it finds in the game's item database. If we changed the offset to 2, then it would still match 4 gems, but instead of matching the very first 4, it would match the 3rd, 4th, 5th, and 6th gems.
Limits and Offsets can be especially useful if you want to divide a bag up into multiple chunks. For example, suppose you wanted a bag that matched all fish, but you have a ton of mods that add fishes to the game. So a single bag for all fish might contain too many items for them to fit on screen. If you wanted to break it up into multiple bags, you might set the 1st bag to use Limit=100, Offset=0, then the 2nd bag uses Limit=100, Offset=100, the 3rd bag uses Limit=100, Offset=200 etc.
You can also apply a Limit and Offset directly to an individual filter, by suffixing the filter's name with a hyphen followed by the limit and offset, like so:
"CategoryId-4,0:-2"
CategoryId is the filter type
4,0 is the limit and offset
-2 is the value that the filter matches
"{FilterType}-{Limit},{Offset}:{Value}"
Here's a more complex example:
"ItemFilters": [
"!Quality:Iridium|CategoryId-5,0:-2",
"CategoryId:-2"
],
Don't ask me why you'd ever do this, but the above example would match all gems, except that it would exclude the iridium quality on the 1st 5 gems.
Note: If Limit is set to 0, it is implicitly treated as infinite
Quality levels
For example, suppose you installed a mod that allowed gems to be available in silver/gold/iridium qualities. If you then wanted to use ItemFilters to make a modded bag that can store all gem items, you could do the following:
{
...
"ItemFilters": [
"CategoryId:-2"
],
"CategoryQualities": "-2:true"
...
}
"-2:true" tells ItemBags that the category Id of -2 (Gems) DOES support multiple quality values. So the format is: "{CategoryId}:{OverrideValue}" where the OverrideValue can either be 'true' or 'false'. You can override multiple categories by separating them with a comma, such as "-2:true,-4:false"
Debugging
Changelog
- 3.1.0-beta1:
- 3.1.0-beta2:
- 3.1.0-beta3:
- 3.1.0-beta4:
Improve BagSize filter to support the following prefixes: '<', '>', '<=', '>=', '='
Add HasMod filter
- 3.1.0-beta5:
Items in bag menus are no longer grouped by quality unless they support all 4 quality values being placed into the bag
Quality ItemFilter value is now case-insensitive
HasContextTag filter now handles tags added dynamically after item creation (such as "HasContextTag:fish_difficulty_easy")
- 3.1.0-beta6:
Add refresh_modded_bags console command to aid with testing so you can see your changes without relaunching the game
Improved error-logging for invalid filters
- 3.1.0-beta7:
Add Limit/Offset settings
Add Regex filters
Add settings to control item sorting order
41 comments
{
"IsEnabled": true,
"ModUniqueId": "SlayerDharok.Item_Bags",
"BagId": "d2741cd3-6c98-416b-5a59-51ca88672751",
"BagName": "Cornucopia Artisan Goods Bag",
"BagDescription": "A bag for storing items of specific category(s)",
"IconTexture": "SpringObjects",
"IconPosition": {
"X": 0,
"Y": 0,
"Width": 0,
"Height": 0
},
[...]
"ItemFilters": [
"FromMod:Cornucopia.ArtisanMachines|FromMod:Cornucopia.MoreCrops|FromMod:Cornucopia.MoreFlowers",
"CategoryId:-5|CategoryId:-6|CategoryId:-25|CategoryId:-26|CategoryId:-27"
]
}
The cornucopia mods seem to name their items like this instead: "Cornucopia_{ItemId}" so they aren't detected.
You'd have to use a LocalIdPrefix filter instead, such as: "LocalIdPrefix:Cornucopia_"
Alternatively you could try using the HasContextTag filter since it seems like those mods add their own context tags that identify the mod, such as "HasContextTag:modid_cornucopia.morecrops"
Or filter for the category: "CategoryId:-23"
Or filter for a a context tag they share in common: "HasContextTag:forage_item_beach"
"ItemFilters": [
"!IsVanillaItem",
"HasContextTag:item_type_arch"
]
Also the "Quality" item filter feels weird since even if a bag only accepts iridium quality (for example), the bag will still show all qualities regardless. I remember reading about the Qualities being hardcoded or something so I am guessing this is intentional?
The quality filter was mainly intended to be used with menus that set "GroupByQuality" to false. Right now, if GroupByQuality is true, then any item that supports multiple quality values is being displayed in the grouped portion of the menu. I'll change this so that it only groups items that support all 4 item qualities.
Also there's currently a bug with the quality filter where it expects a lowercase value (I.E. "Quality:Gold" wouldn't work, currently needs "Quality:gold"). Fixing that in next update.
"! CategoryId:-7" should be "!CategoryId:-7" for example.
Also, are you logically OR-ing those category filters? If so, it wouldn't make much sense. If you logically OR two negated filters that match mutually-exclusive values, then the result set would be every single item. For example, "(NOT fish OR NOT fruit)" would match everything, because there is no such item that is both a fish and a fruit.
"ItemFilters": [
"FromMod:FlashShifter.StardewValleyExpandedCP"
"CategoryId:-4"
],
An error will occur
The errors are as follows
[Item Bags] Error while loading modded bag json files: Can't parse JSON file at C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Mods\ItemBags\assets\Modded Bags\Stardew Valley Expanded Fish Bag.json. This doesn't seem to be valid JSON.
Technical details: After parsing a value an unexpected character was encountered: ". Path 'ItemFilters[0]', line 130, position 2.
Newtonsoft.Json.JsonReaderException: Can't parse JSON file at C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Mods\ItemBags\assets\Modded Bags\Stardew Valley Expanded Fish Bag.json. This doesn't seem to be valid JSON.
Technical details: After parsing a value an unexpected character was encountered: ". Path 'ItemFilters[0]', line 130, position 2.
at StardewModdingAPI.Toolkit.Serialization.JsonHelper.ReadJsonFileIfExists[TModel](String fullPath, TModel& result) in /home/pathoschild/git/SMAPI/src/SMAPI.Toolkit/Serialization/JsonHelper.cs:line 86
at StardewModdingAPI.Framework.ModHelpers.DataHelper.ReadJsonFile[TModel](String path) in /home/pathoschild/git/SMAPI/src/SMAPI/Framework/ModHelpers/DataHelper.cs:line 50
at ItemBags.ItemBagsMod.LoadModdedBags() in
C:\Programming\Source\Personal\SDV\Public\ItemBags\ItemBags\ItemBagsMod.cs:line 409
"ItemFilters": [
"FromMod:FlashShifter.StardewValleyExpandedCP",
"CategoryId:-4"
],
[Item Bags] Error while loading modded bag json files: Can't parse JSON file at /Users/embergamer/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS/Mods/expansions/ItemBags/assets/Modded Bags/emberonis_EisforEggs.json. This doesn't seem to be valid JSON.Technical details: After parsing a value an unexpected character was encountered: ". Path 'ItemFilters[0]', line 138, position 1.Newtonsoft.Json.JsonReaderException: Can't parse JSON file at /Users/embergamer/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS/Mods/expansions/ItemBags/assets/Modded Bags/emberonis_EisforEggs.json. This doesn't seem to be valid JSON.Technical details: After parsing a value an unexpected character was encountered: ". Path 'ItemFilters[0]', line 138, position 1. at StardewModdingAPI.Toolkit.Serialization.JsonHelper.ReadJsonFileIfExists[TModel](String fullPath, TModel& result) in E:\source\_Stardew\SMAPI\src\SMAPI.Toolkit\Serialization\JsonHelper.cs:line 86 at StardewModdingAPI.Framework.ModHelpers.DataHelper.ReadJsonFile[TModel](String path) in E:\source\_Stardew\SMAPI\src\SMAPI\Framework\ModHelpers\DataHelper.cs:line 50 at ItemBags.ItemBagsMod.LoadModdedBags() in C:\Programming\Source\Personal\SDV\Public\ItemBags\ItemBags\ItemBagsMod.cs:line 409
And here's what I added into my JSON file, copy/pasted from the tutorial and then edited (items section left in for context):
"Items": [],
"ItemCategories": {
"Small": [],
"Medium": [],
"Large": [],
"Giant": [],
"Massive": []
},
"ItemFilters": [
"!FromMod:UncleArya.ResourceChickens"
"CategoryId:-5"
]
}
If I add a comma after the !FromMod section, the JSON loads, but the resulting bag just shows items from the mod. I've also toyed a little with using the | in test bags, and that hasn't worked either. But so far I've had no issues with modded bags that only use a single filter, so that's good!
"!FromMod:UncleArya.ResourceChickens"
To this:
"!LocalIdPrefix:UncleArya.ResourceChickens."
And it should be able to correctly identify that mod's items.
{ "IsEnabled": true,
"ModUniqueId": "FlashShifter.StardewValleyExpandedCP",
"BagId": "a12c1fcb-9220-e0a8-420e-0e4a1aeb3d64",
"BagName": "Stardew Valley Expanded Fish Bag",
"BagDescription": "A bag for storing items belonging to Stardew Valley Expanded Fish ",
"IconTexture": "SpringObjects",
"IconPosition": { "X": 0, "Y": 0, "Width": 0, "Height": 0 },
"Prices":{
"Small": 2000,
"Medium": 5000,
"Large": 20000,
"Giant": 50000,
"Massive": 100000
},
"Capacities": {
"Small": 30,
"Medium": 99,
"Large": 300,
"Giant": 999,
"Massive": 9999
},
"SizeSellers": {
"Small": [ " Willy" ],
"Medium": [ " Willy" ],
"Large": [ " Willy" ],
"Giant": [ " Willy" ],
"Massive": [ " Willy" ] },
"SizeMenuOptions": {
"Small": {
"GroupByQuality": true,
"InventoryColumns": 12,
"InventorySlotSize": 64,
"GroupedLayoutOptions": {
"GroupsPerRow": 5,
"ShowValueColumn": true,
"SlotSize": 64
},
"UngroupedLayoutOptions": {
"Columns": 12,
"LineBreakIndices": [],
"LineBreakHeights": [],
"SlotSize": 64
}
},
"Medium": {
"GroupByQuality": true,
"InventoryColumns": 12,
"InventorySlotSize": 64,
"GroupedLayoutOptions": {
"GroupsPerRow": 5,
"ShowValueColumn": true,
"SlotSize": 64
},
"UngroupedLayoutOptions": {
"Columns": 12,
"LineBreakIndices": [],
"LineBreakHeights": [],
"SlotSize": 64
}
},
"Large": {
"GroupByQuality": true,
"InventoryColumns": 12,
"InventorySlotSize": 64,
"GroupedLayoutOptions": {
"GroupsPerRow": 5,
"ShowValueColumn": true,
"SlotSize": 64
},
"UngroupedLayoutOptions": {
"Columns": 12,
"LineBreakIndices": [],
"LineBreakHeights": [],
"SlotSize": 64
}
},
"Giant": {
"GroupByQuality": true,
"InventoryColumns": 12,
"InventorySlotSize": 64,
"GroupedLayoutOptions": {
"GroupsPerRow": 5,
"ShowValueColumn": true,
"SlotSize": 64
},
"UngroupedLayoutOptions": {
"Columns": 12,
"LineBreakIndices": [],
"LineBreakHeights": [],
"SlotSize": 64
}
},
"Massive": {
"GroupByQuality": true,
"InventoryColumns": 12,
"InventorySlotSize": 64,
"GroupedLayoutOptions": {
"GroupsPerRow": 5,
"ShowValueColumn": true,
"SlotSize": 64
},
"UngroupedLayoutOptions": {
"Columns": 12,
"LineBreakIndices": [],
"LineBreakHeights": [],
"SlotSize": 64
}
}
},
"Items": [ ],
"ItemFilters": [
"FromMod:FlashShifter.StardewValleyExpandedCP"
],
}