Difference between revisions of "Modding:Migrate to Stardew Valley 1.6"

From Stardew Valley Wiki
Jump to navigation Jump to search
(→‎Game state queries: update for recent changes)
(→‎Item references: add mention of qualified vs unqualified format in data assets, start table by asset)
Line 87: Line 87:
  
 
===Item references===
 
===Item references===
Item references throughout the game code now use the <samp>ItemID</samp> (and sometimes <samp>QualifiedItemID</samp>) instead of the <samp>ParentSheetIndex</samp>. Since the <samp>ItemID</samp> is identical to the index for existing vanilla items, most data assets are unaffected by this change. For example, here's from <samp>Data/NPCDispositions</samp> with one custom item:
+
Item references throughout the game code now use the <samp>ItemID</samp> instead of the <samp>ParentSheetIndex</samp>. Since the <samp>ItemID</samp> is identical to the index for existing vanilla items, most data assets are unaffected by this change. For example, here's from <samp>Data/NPCDispositions</samp> with one custom item:
 
<syntaxhighlight lang="json">
 
<syntaxhighlight lang="json">
 
"Universal_Like": "-2 -7 -26 -75 -80 72 395 613 634 635 636 637 638 724 459 Example.ModID_watermelon"
 
"Universal_Like": "-2 -7 -26 -75 -80 72 395 613 634 635 636 637 638 724 459 Example.ModID_watermelon"
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Unless otherwise noted, unqualified item IDs will produce [[Modding:Object data|objects]]. Some assets let you override that by specifying a <samp>QualifiedItemID</samp> value instead. For example, you can add <code>(O)128</code> to the gift taste list to explicitly add for an object. Here's a partial list of data assets and their supported item ID formats:
 +
 +
{| class="wikitable"
 +
|-
 +
! data asset
 +
! item ID format
 +
|-
 +
| [[Modding:Recipe data|<samp>Data/CraftingRecipes</samp><br /><samp>Data/CookingRecipes</samp>]]
 +
| &#32;
 +
* Ingredients: both supported.
 +
* Output: set field 2 to the unqualified item ID, and field 3 to one of <samp>true</samp> (bigcraftable) or <samp>false</samp> (object) or an item type identifier like <samp>BC</samp>.
 +
|-
 +
| [[#Custom fruit trees|<samp>Data/fruitTrees</samp>]]
 +
| &#32;
 +
* Fruit: both supported.
 +
* Sapling: unqualified only.
 +
|-
 +
| [[Modding:Gift taste data|<samp>Data/NPCGiftTastes</samp>]]
 +
| Both supported, but only <samp>(O)</samp> items can be gifted.
 +
|}
  
 
===Define a custom item===
 
===Define a custom item===

Revision as of 02:43, 26 January 2022

Index

This page is for mod authors. Players: see Modding:Mod compatibility instead.

The following describes the upcoming SMAPI 4.0.0, and may change before release.

This page explains how to update your mods for compatibility with the next major game version (tentatively Stardew Valley 1.6.0), and documents some of the changes and new functionality.

This describes an unreleased alpha version of the game. Things will change before it's released!

FAQs

What's changing?

Stardew Valley 1.6 makes fundamental changes to the game code to make it more extensible for mods.

Is this the modapocalypse?

Yes. The update includes major changes to fundamental parts of the game, and SMAPI and Content Patcher can't feasibly rewrite older mods for compatibility with these changes. This will break a large proportion of existing mods until they're updated for the changes. However, per discussion between the game developers and modding community, we've agreed that this short-term pain is more than offset by the huge long-term improvements to modding.

Which changes are likely to break mods?

How to update your mod

  1. Update your mod code and assets for any breaking changes listed below.
  2. If SMAPI still says your mod is incompatible, check the TRACE messages in the log file for the reason why.
    If the logs say "marked 'assume broken' in SMAPI's internal compatibility list", you can increase the Version in your content pack's manifest.json file to bypass it.
  3. Test the mod in-game and make any other changes needed.

Buff overhaul

1.6 rewrites buffs to work more consistently and be more extensible:

  • Buff logic is unified into Game1.player.buffs, which is the single source of truth for buff data. This replaces the previous player.added* and player.appliedBuffs fields, BuffsDisplay logic, enchantment stat bonuses, and boots/ring attribute changes on (un)equip.
  • This also removes limitations on buff types (e.g. buffs can add weapon bonuses and weapons can add attribute buffs) and buffable equipment (e.g. equipped tools can have buffs too).
  • Buff effects are now fully recalculated when they change, to fix a range of longstanding bugs like attribute drift and double-debuffs. Just like before, the buffs are managed locally; only the buff IDs and aggregate attribute effects are synced.

For C# mods:

  • Each buff now has a unique string ID. You can apply a new buff with the same ID to replace it (so you no longer need to manually find and remove previous instances of the buff).
  • You can add standard buff effects to any equipment by overriding Item.AddEquipmentEffects, or add custom behaviour/buffs by overriding Item.onEquip and Item.onUnequip.
  • You can add custom food or drink buffs by overriding Item.GetFoodOrDrinkBuffs().
  • The Buff constructor now supports a custom icon texture, sprite index, display name, description, and millisecond duration to fully support custom buffs.
  • You can change how buff attributes are displayed (or add new attributes) by extending the BuffsDisplay.displayAttributes list.

For example, here's how to add a custom buff which adds +3 speed:

Buff buff = new Buff(
    buff_id: "Example.ModId/ZoomZoom",
    display_name: "Zoom Zoom", // can optionally specify description text too
    icon_texture: this.Helper.Content.Load<Texture2D>("assets/zoom.png"),
    icon_sheet_index: 0,
    duration: 30_000, // 30 seconds
    buff_effects: new BuffEffects()
    {
        speed = { 10 } // shortcut for buff.speed.Value = 10
    }
);
Game1.player.applyBuff(buff);

You can also implement your own custom effects in code by checking if the buff is active, like Game1.player.hasBuff("Example.ModId/ZoomZoom").

Custom items

Overview

Stardew Valley 1.6 makes three major changes to how items work in the game:

  1. Each item type now has an ItemDataDefinition class which tells the game how to handle it. SMAPI mods can add custom item type definitions or patch the vanilla logic. Each definition has a unique prefix (like (O) for objects) which is used in qualified item IDs. The vanilla types are bigcraftables ((BC)), boots ((B)), furniture ((F)), hats ((H)), objects ((O)), pants ((P)), shirts ((S)), tools ((T)), and weapons ((W)).
  2. Each item now has a locally unique string ID (ItemID) and a globally unique string ID (QualifiedItemID). The ItemID is assumed to only contain alphanumeric/underscore/dot characters so they can be used in fields delimited with spaces/slashes/commas, in filenames, etc. The QualifiedItemID is auto-generated by prefixing the ItemID with the item type identifier.

    For legacy reasons, the ItemID for vanilla item isn't globally unique. For example, Pufferfish (object 128) and Mushroom Box (bigcraftable 128) both have ItemID: 128. They can be distinguished by their QualifiedItemID, which is (O)128 and (BC)128 respectively.

    For mod items, both IDs should be globally unique. By convention the ItemID should include your mod ID or author name, like Example.ModId_ItemName.

  3. Custom items can now provide their own item texture, specified in a new field in the item data assets (see below). The item's ParentSheetIndex field is the index within that texture.

In other words, the three important fields for items are:

name type description
ItemID string An item key which is only unique within its item type, like 128 (vanilla item) or Example.ModId_Watermelon (custom item).
QualifiedItemID string A globally unique item key, like (O)128 (vanilla item) or (O)Example.ModId_Watermelon (custom item).
ParentSheetIndex int The item's image sprite within its spritesheet (whether it's a vanilla spritesheet or custom one).

Item references

Item references throughout the game code now use the ItemID instead of the ParentSheetIndex. Since the ItemID is identical to the index for existing vanilla items, most data assets are unaffected by this change. For example, here's from Data/NPCDispositions with one custom item:

"Universal_Like": "-2 -7 -26 -75 -80 72 395 613 634 635 636 637 638 724 459 Example.ModID_watermelon"

Unless otherwise noted, unqualified item IDs will produce objects. Some assets let you override that by specifying a QualifiedItemID value instead. For example, you can add (O)128 to the gift taste list to explicitly add for an object. Here's a partial list of data assets and their supported item ID formats:

data asset item ID format
Data/CraftingRecipes
Data/CookingRecipes
  • Ingredients: both supported.
  • Output: set field 2 to the unqualified item ID, and field 3 to one of true (bigcraftable) or false (object) or an item type identifier like BC.
Data/fruitTrees
  • Fruit: both supported.
  • Sapling: unqualified only.
Data/NPCGiftTastes Both supported, but only (O) items can be gifted.

Define a custom item

Overview

You can define custom items for most vanilla item types using only Content Patcher or SMAPI's content API. The data asset for each item type has two new fields:

field effect
texture name The asset name for the texture under the game's Content folder. Use \ (or \\ in JSON) to separate name segments if needed. For example, objects use Maps\springobjects by default.
sprite index The index of the sprite within the above texture, starting at 0 for the top-left sprite.

Supported item types:

item type data asset sprite index index texture name index default texture name
big craftables Data/BigCraftablesInformation 10 11 TileSheets/Craftables
boots Data/Boots item sprite: 8
shoe color: 5
item sprite: 9
shoe color: 7
item sprite: Maps/springobjects
shoe color: Characters/Farmer/shoeColors
crops Data/Crops 2 9 TileSheets/crops
furniture Data/Furniture 8 9 TileSheets/furniture
fruit trees Data/FruitTrees 0 4 TileSheets/fruitTrees
hats Data/Hats 6 7 Characters/Farmer/hats
objects Data/ObjectInformation 9 10 Maps/springobjects
pants & shirts Data/ClothingInformation male: 3
female: 4
10 Characters/Farmer/pants
Characters/Farmer/shirts
tools Data/ToolData 3 4 TileSheets/Tools
weapons Data/Weapons 15 16 TileSheets/weapons

For example, this content pack adds a new Pufferchick item with a custom image, custom gift tastes, and a custom crop that produces it. Note that item references in other data assets like Data/Crops and Data/NPCGiftTastes use the item ID.

{
    "Format": "2.0.0",
    "Changes": [
        // add item
        {
            "Action": "EditData",
            "Target": "Data/ObjectInformation",
            "Entries": {
                "Example.ModId_Pufferchick": "Pufferchick/1200/100/Seeds -74/Pufferchick/An example object.////0/Mods\\Example.ModId\\Objects"
            }
        },

        // add gift tastes
        {
            "Action": "EditData",
            "Target": "Data/NPCGiftTastes",
            "TextOperations": [
                {
                    "Operation": "Append",
                    "Target": ["Entries", "Universal_Love"],
                    "Value": "Example.ModId_Pufferchick",
                    "Delimiter": " " // if there are already values, add a space between them and the new one
                }
            ]
        },

        // add crop (Pufferchick is both seed and produce, like coffee beans)
        {
            "Action": "EditData",
            "Target": "Data/Crops",
            "Entries": {
                "Example.ModId_Pufferchick": "1 1 1 1 1/spring summer fall/0/Example.ModId_Pufferchick/-1/0/false/false/false/Mods\\Example.ModId\\Crops"
            }
        },

        // add item + crop images
        {
            "Action": "Load",
            "Target": "Mods/Example.ModId/Crops, Mods/Example.ModId/Objects",
            "FromFile": "assets/{{TargetWithoutPath}}.png" // assets/Crops.png, assets/Objects.png
        },
    ]
}

Most item data assets work just like Data/ObjectInformation. For fruit trees, see custom fruit trees.

Tools

Tools are defined in the new Data/ToolData asset using this field format:

index field purpose
0 Class The name of the class in the StardewValley.Tools namespace. The class must have a constructor with no arguments. For example, given a value of Axe, the game will create StardewValley.Tools.Axe instances.
1
2
Name key
Description key
The string key to load for the tool's in-game display name and description, in the form <asset name>:<key> (e.g., Strings\StringsFromCSFiles:Axe.cs.1). In JSON, use \\ for any asset path slashes.
3 Parent sheet index The index of the tool's sprite in its spritesheet. Tool upgrades are handled by adding an offset to this index (e.g., basic axe = index, copper axe = index + 1, steel axe = index + 2, etc).
4 Texture (Optional) The asset name for the item's spritesheet.

Error items

In-game items with no underlying data (e.g. because you removed the mod which adds them) would previously cause issues like invisible items, errors, and crashes. This was partly mitigated by the bundled ErrorHandler mod.

Stardew Valley 1.6 adds comprehensive handling for such items. They'll be shown with a 🛇 sprite in inventory UIs and in-game, with the name Error Item and a description which indicates the missing item ID for troubleshooting.

For C# mods

Compare items
Since Item.QualifiedItemID is globally unique, it can be used to simplify comparing items. For example:
old code new code
item.ParentSheetIndex == 848 item.QualifiedItemID == "(O)848"
IsNormalObjectAtParentSheetIndex(item, 74) item.QualifiedItemID == "(O)74"
!item.bigCraftable && item.ParentSheetIndex == 128 item.QualifiedItemID == "(O)128"
item is Boots && item.ParentSheetIndex == 505 item.QualifiedItemID == "(B)505"

Caveat: flavored item don't have their own ID. For example, Blueberry Wine and Wine are both (O)348. This affects flavored jellies, juices, pickles, and wines. In those cases you should still compare their separate fields like preservedParentSheetIndex (which actually contains the preserved item's ItemID, not its ParentSheetIndex).

Construct items
Creating items works just like before, except that you now specify the item's ItemID (_not_ QualifiedItemID) instead of its ParentSheetIndex. For example:
new Object("634", 1);                      // vanilla item
new Object("Example.ModId_Watermelon", 1); // custom item

You can use a new utility method to construct items from their QualifiedItemID:

Item item = Utility.CreateItemByID("(B)505"); // Rubber Boots
Define custom item types
You can subclass ItemDataDefinition for your own item type, and add an instance to the ItemDataDefinition.ItemTypes and IdentifierLookup lists. This provides all the logic needed by the game to handle the item type: where to get item data, how to draw them, etc. This is extremely specialized, and multiplayer compatibility is unknown. Most mods should add custom items within the existing types instead.
New Is* methods
1.6 adds some StardewValley.Object methods to handle custom items in a generic way (and to let mods patch the logic):
method effect
object.IsTeaSapling() Whether the item is a Tea Sapling.
object.IsFruitTreeSapling() Whether the item is a fruit tree sapling. This checks the Data\fruitTrees keys, so it works with custom fruit trees too.
object.IsTapper() Whether the item is a Tapper or Heavy Tapper.
object.IsBar() Whether the item is a Copper Bar, Iron Bar, Gold Bar, Iridium Bar, or Radioactive Bar.

Custom map areas

The valley map, with one map area highlighted.

You can now add/edit areas on the game menu's map UI, or even replace the entire map for a different world region, by editing the new Data/InGameMap asset. For example, this is used to show a different farm on the map depending on the current farm type.

Concepts

The game divides the map into three concepts:

  • A map is one full world area rendered in one image, like the entire image shown at right.
  • A map area is a smaller section of the map which is linked to one or more in-game areas. The map area might be edited/swapped depending on the context, have its own tooltip(s), or have its own player marker positions. For example, the highlighted area on the image shown at right is swapped depending on the current farm type.
  • A group name is a unique key shared between all the map areas that are rendered on the same map. For example, if there was a separate map for Ginger Island, all the map areas in the valley group would be hidden.

Format

The Data/InGameMap data asset consists of a string → model lookup, where the key is a unique identifier for the map area, and the value is a model with these fields:

field effect
AreaID A unique identifier for the area. This should match the key.
Group A unique group key shared between all areas drawn on the same map (see concepts). When the player is in an area for one group, all map areas with a different group key are hidden automatically. The base valley map added by the base game has the group key SDV.
Texture The texture asset name to draw when the area is applied to the map.

If set to the exact string "MOD_FARM", the game will apply the texture for the current farm type (regardless of whether it's a vanilla or mod farm type).

Zones The in-game locations to match with the map. Each zone can provide one or more of the following:
  • match an in-game location to the world map area (via ValidAreas);
  • match an in-game tile position to the world map pixel position (via MapTileCorners and MapImageCorners);
  • add tooltips and scroll text for different portions of the world map (via DisplayName).

This consists of a list models with these fields:

field effect
ValidAreas A list of location names. Each location can be one of...
  • An internal location name (as shown by the Debug Mode mod). Any location within the mines and the Skull Cavern will be Mines and SkullCave respectively, and festivals use the map asset name (e.g. Town-EggFestival). For example, the vanilla desert uses "ValidAreas": [ "Desert", "SkullCave", "SandyHouse", "SandyShop", "Club" ].
  • CONTEXT_Default for any location in the valley.
  • CONTEXT_Island for any location on Ginger Island.
MapTileCorners
MapImageCorners
(Optional) The corner coordinates for the in-game location measured in tiles, and the equivalent map image area measured in pixels. Both use the form "<top-left x> <top-left y> <bottom-right x> <bottom-right y>".

These are used to calculate the position of a player within the map view, given their real position in-game. For example, let's say an area has tile positions (0, 0) through (10, 20), and map pixel positions (200, 200) through (300, 400). If the player is standing on tile (5, 10) in-game (in the exact middle of the location), the game would place their marker at pixel (250, 300) on the map (in the exact middle of the map area).

If these are omitted, the player marker is simply placed in the center of the parent map area.

DisplayName (Optional) A tokenizable string for the scroll text (shown at the bottom of the map when the player is in this area). Defaults to the parent area's DisplayName. If this is the exact string FarmName, it shows the farm name instead.
DisplayName (Optional) A tokenizable string for the tooltip (shown when the mouse is over the area) and scroll (shown at the bottom of the map when the player is in the location). If omitted or empty, the tooltip/scroll isn't shown. Defaults to empty.
Condition (Optional) A game state query which checks whether the area should be applied (if the group is visible). Defaults to always applied.
DisplayAsUnknown (Optional) Whether to replace the tooltip for the area with ??? if the Condition isn't met. Default false.
SourceRect (Optional) The pixel area within the texture asset name to draw, formatted like "<x> <y> <width> <height>". Defaults to the entire texture.
DestRect (Optional) The pixel area within the map onto which to apply the area if it matches. Defaults to the top-left corner of the map.
IncludeInGroupDetection (Optional) Whether to use this area when the game is scanning areas to see which group the player is in. If this is false, it will be ignored even if the player is in the area. Default true.

Example

This Content Patcher content pack adds a full world map for Ginger Island, complete with tooltips and player marker positioning. If the player unlocked the beach resort, it applies the beach resort texture on top of the base map.

{
    "Format": "2.0.0",
    "Changes": [
        // add world map edits
        {
            "Action": "EditData",
            "Target": "Data/IngameMap",
            "Entries": {
                // the base world map image with tooltips/positioning
                "GingerIsland_BaseMap": {
                    "Group": "GingerIsland",
                    "AreaID": "GingerIsland_BaseMap",
                    "Texture": "Mods/Example.ModId/GingerIsland",
                    "SourceRect": "0 0 300 180",
                    "DestRect": "0 0 300 180",
                    "Zones": [
                        {
                            "ValidAreas": [ "IslandSouth" ],
                            "MapTileCorners": "0 0 42 45",
                            "MapImageCorners": "105 105 231 240",
                            "DisplayName": "{{i18n: map-names.island-south}}"
                        },
                        // ... list each map area here
                    ]
                },

                // add beach resort if unlocked
                "GingerIsland_Resort": {
                    "Group": "GingerIsland",
                    "AreaID": "GingerIsland_Resort",
                    "Texture": "Mods/Example.ModId/GingerIsland_Resort",
                    "SourceRect": "0 0 300 180",
                    "DestRect": "0 0 300 180",
                    "Condition": "PLAYER_HAS_FLAG Any Island_Resort"
                }
            }
        },

        // load textures
        {
            "Action": "Load",
            "Target": "Mods/Example.ModId/GingerIsland",
            "FromFile": "assets/ginger-island.png"
        }
    ]
}

Custom shops

Format

You can now create and edit shops via the new Data/Shops asset. This consists of a list of models with these fields:

field effect
ID A unique ID for the shop. The current vanilla shop IDs are Marnie (Marnie's Ranch), Marlon (Adventurer's Guild), and Pierre (Pierre's General Store). For a custom shop, you should use a globally unique ID which includes your mod ID like ExampleMod.Id_ShopName.
ItemGroups The items to list in the shop inventory. This consists of a list of models with these fields:
field effect
Items The items to add to the shop inventory if the Condition matches. This costs of a list of values with these fields:
field effect
ItemID One of the qualified item ID (like (O)128 for a pufferfish), or SEEDSHOP_RANDOM_WALLPAPERS (random wallpapers), or SEEDSHOP_PLAYER_ITEMS (items the player has recently sold to Pierre's store).
IsRecipe (Optional) Whether to add the crafting/cooking recipe for the item, instead of the item itself. Default false.
Price (Optional) The price to purchase the item from the shop. Defaults to the item's normal price.
IgnorePriceMultiplier (Optional) Whether to ignore the PriceMultiplier value set for the shop. Default false.
Stock (Optional) The maximum number of the item which can be purchased in one day. Default unlimited.
Condition (Optional) A game state query which indicates whether the item should be added. If omitted, the item is always added.

Special case: if the player found Pierre's Missing Stocklist, season conditions are ignored in Pierre's General Store.

ShopOwner (Optional) The name of the NPC whose portrait to show in the shop UI.
Dialogues (Optional) A list of possible dialogues; each day one dialogue will be randomly chosen to show in the shop UI. Each dialogue consists of a model with these fields:
field effect
Dialogue The dialogue text to show, as a tokenizable string. The resulting text is parsed using the dialogue format.
Condition (Optional) A game state query which indicates whether the dialogue should be available. If omitted, the dialogue is always available.
PriceMultiplier (Optional) A multiplier applied to the price when buying items from the shop, like 1.5 for a 50% markup. Defaults to 1.

Note: this is only applied to items in ItemGroups which explicitly set a price.

Open a custom shop

In C# code, you can get the inventory for a custom shop using Utility.GetShopStock("shop id here"), or open a shop menu using Game1.activeClickableMenu = new ShopMenu(new(), who: "shop id here").

TODO: look into map tile property to open a custom shop.

Custom fruit trees

You can now add custom fruit trees by editing the modified Data/fruitTrees asset. This consists of a stringstring lookup, where the key is the sapling item ID and the value is a slash (/)-delimited list of these fields:

index field effect
0 tree ID A key which uniquely identifies this tree. For vanilla trees, this matches the spritesheet index. For custom trees, the ID should only contain alphanumeric/underscore/dot characters.
1 season The season in which the fruit tree bears fruit.
2 fruit The unqualified object ID for the fruit it produces.
3 sapling price Unused.
4 sprite index The tree's row index in the spritesheet (e.g. 0 for the first tree, 1 for the second tree, etc). If omitted, the game will try to parse the item ID as an index.
5 texture The asset name for the texture under the game's Content folder. Use \ (or \\ in JSON) to separate name segments if needed. For example, fruit trees use TileSheets\fruitTrees by default.

For example, this content pack adds a custom fruit tree, including custom items for the sapling and fruit:

{
    "Format": "2.0.0",
    "Changes": [
        // add fruit + sapling items
        // note: sapling must have an edibility under 0 (usually -300) to be plantable
        {
            "Action": "EditData",
            "Target": "Data/ObjectInformation",
            "Entries": {
                "Example.ModId_Pufferfruit": "Pufferfruit/1200/100/Basic -6/Pufferfruit/An example fruit item.////1/Mods\\Example.ModId\\Objects",
                "Example.ModId_Puffersapling": "Puffersapling/1200/-300/Basic -74/Puffersapling/An example tree sapling.////2/Mods\\Example.ModId\\Objects"
            }
        },

        // add fruit tree
        {
            "Action": "EditData",
            "Target": "Data/FruitTrees",
            "Entries": {
                "Example.ModId_Puffersapling": "Example.ModId_Puffertree/spring/Example.ModId_Pufferfruit/1000/0/Mods\\Example.ModId\\FruitTrees"
            }
        },

        // add images
        {
            "Action": "Load",
            "Target": "Mods/Example.ModId/FruitTrees, Mods/Example.ModId/Objects",
            "FromFile": "assets/{{TargetWithoutPath}}.png" // assets/FruitTrees.png, assets/Objects.png
        },
    ]
}

The fruit trees can then be added to the game by giving the player a sapling item in the usual ways (e.g. from a shop).

Custom wild trees

You can now create/edit wild trees by editing the Data/WildTrees asset. This consists of a string => model lookup, where the asset key is the wild tree ID: one of 1 (oak), 2 (maple), 3 (pine), 6 (palm), 7 (mushroom), 8 (mahogany), or a custom string ID defined by a mod. The asset value is a model with these fields:

field effect
TreeType The tree ID; this should match the asset key.
Textures The tree textures to show in game. This can be a list containing either a single asset name, or four asset names (for spring, summer, fall, and winter in that order).
SeedItemID The qualified item ID for the seed item.
SeedPlantable (Optional) Whether the seed can be planted by the player. If this is false, it can only be spawned automatically via map properties. Default true.
GrowthChance (Optional) The probability each day that the tree will grow to the next stage without tree fertilizer, expressed as a value from 0 (will never grow) to 1 (will grow every day). Defaults to 0.2 (20% chance).
FertilizedGrowthChance (Optional) Equivalent to GrowthChance, but with tree fertilizer. Defaults to 1 (100% chance).
SeedChance (Optional) The probability each day that the tree will produce a seed that will drop when the tree is shaken. Default 0.05 (5% chance).
SeedOnChopChance (Optional) The probability that a seed will drop when the player chops down the tree. Default 0.75 (75% chance).
DropWoodOnChop (Optional) Whether to drop wood when the player chops down the tree. Default true.
DropHardwoodOnLumberChop (Optional) Whether to drop hardwood when the player chops down the tree, if they have the Lumberjack profession. Default true.
IsLeafy (Optional) Whether shaking or chopping the tree causes cosmetic leaves to drop from tree and produces a leaf rustle sound. When a leaf drops, the game will use one of the four leaf sprites in the tree's spritesheet in the slot left of the stump sprite. Default true.
IsLeafyInWinter (Optional) Whether IsLeafy also applies in winter. Default false.
GrowsInWinter (Optional) Whether the tree can grow in winter (subject to GrowthChance and FertilizedGrowthChance). Default false.
IsStumpDuringWinter (Optional) Whether the tree is reduced to a stump in winter and regrows in spring, like the vanilla mushroom tree. Default false.
AllowWoodpeckers (Optional) Whether woodpeckers can spawn on the tree. Default true.
UseAlternateSeedSprite (Optional) Whether to render a different tree sprite when it has a seed ready. If true, the tree spritesheet should be double-width with the alternate textures on the right. Default false.
DebrisColor (Optional) The color of the cosmetic wood chips when chopping the tree. The valid values are 12 (brown/woody), 100001 (light green), 100002 (light blue), 100003 (red), 100004 (yellow), 100005 (black), 100006 (gray), 100007 (charcoal / dim gray), or any other value for white. Defaults to brown/woody.
AdditionalChopDrops
BushChopDrops
StumpChopDrops
(Optional) The additional items to drop when the tree is chopped. One field applies depending on the tree's current state: AdditionalChopDrops for a full-grown tree, BushChopDrops for a bush (one step below full-grown), and StumpChopDrops for the stump left behind after chopping a full-grown tree. This consists of a list of models with these fields:
field effect
ItemID The qualified item ID.
MinCount
MaxCount
The minimum/maximum number of the item to drop.
Chance (Optional) The probability that the item will drop, as a value between 0 (never drops) and 1 (always drops). This has no effect on other drops (e.g. if there are ten drops with 100% chance, all ten will drop). Default 1 (100% chance).
Condition (Optional) If set, the drop is only available if the given game state query is true.
SpringTapItems
SummerTapItems
FallTapItems
WinterTapItems
The items produced by tapping the tree in each season, as a list of models with these fields. If multiple items can be produced, the first available one is selected.
field effect
PossibleItems The possible items to produce, as a list of models with these fields. If there are multiple items listed, one will be chosen at random each time.
field effect
ItemID The qualified item ID.
MinCount
MaxCount
The minimum/maximum number of the item to produce when the tapper is emptied.
DaysUntilReady The number of days before the tapper is ready to empty.
IsInitialTap (Optional) Whether this group only applies the first time a tree is tapped. Default false.
PreviousItemID (Optional) If set, the group only applies if the previous item produced by the tapper matches the given unqualified item ID.
Condition (Optional) If set, the group only applies if the given game state query is true.

Trees can then be added to the game by...

  • spawning them on map tiles by adding Paths index 34 to the Paths layer, with a TreeType tile property with the tree ID;
  • or giving the player a seed item in the usual ways (e.g. from a shop, mail letter, etc).

Standardized data fields

1.6 standardizes the number of fields in data assets, and fixes inconsistencies between English and localized files. This is a major breaking change for content packs, and for C# mods which edit data assets.

Three examples illustrate the standardization:

  • Data/CookingRecipes had four fields in English, and a fifth field in other languages for the display name. The display name field is now required in English too.
  • Data/BigCraftables had an optional 9th field which indicates whether it's a lamp, and a 10th field for the display name. Even if it's empty, the 9th field is now required (note the extra / before the last field):
    // before
    "151": "Marble Brazier/500/-300/Crafting -9/Provides a moderate amount of light./true/true/0/Marble Brazier", // 9 fields
    
    // after
    "151": "Marble Brazier/500/-300/Crafting -9/Provides a moderate amount of light./true/true/0//Marble Brazier" // 10 fields
    
  • Data/ObjectInformation had several optional fields at the end. These are now required even if empty:
    // before
    "0": "Weeds/0/-1/Basic/Weeds/A bunch of obnoxious weeds."
    
    // after
    "0": "Weeds/0/-1/Basic/Weeds/A bunch of obnoxious weeds.///"
    

Existing mods which add entries without the now-required fields may cause errors and crashes. XNB mods which change data are likely universally broken.

The exception is fields which only exist for modding, like the texture + index overrides for custom items. These can typically be omitted.

String event IDs

Events now use string IDs, so mods can use a unique key like Example.ModId_EventName (instead of hoping no other mod uses the same number). Prefixing the mod ID to event keys is recommended to simplify troubleshooting and avoid conflicts. For best compatibility, custom IDs should only contain alphanumeric/underscore/dot characters.

If an event has no preconditions, you must keep the trailing slash (i.e. have an empty preconditions field) to distinguish it from an event fork:

"Example.ModId_EventName/": "...", // event: loaded automatically by the game
"SomeFork": "..."                  // event fork: ignored unless it's loaded through an event script

New C# utility methods

1.6 adds a new set of utilities (GetDataAtIndex, GetIntAtIndex, and GetFloatAtIndex) to replace common logic for parsing the game's data assets while accounting for optional field indexes.

For example, code like this:

string[] rawEffects = fields.Length > Object.objectInfoBuffTypesIndex && fields[Object.objectInfoBuffTypesIndex].Length > 0
    ? fields[Object.objectInfoBuffTypesIndex].Split(' ')
    : new string[0];

int farming = rawEffects.Length > Buffs.farming && int.TryParse(rawEffects[Buffs.farming], out int _farming)
    ? _farming
    : 0;
int fishing = rawEffects.Length > Buffs.fishing && int.TryParse(rawEffects[Buffs.fishing], out int _fishing)
    ? _fishing
    : 0;

Can now be rewritten like this:

string[] rawEffects = Utility.GetDataAtIndex(fields, Object.objectInfoBuffTypesIndex, "").Split(' ');
int farming = Utility.GetIntAtIndex(rawEffects, Buffs.farming);
int fishing = Utility.GetIntAtIndex(rawEffects, Buffs.fishing);

Game state queries

A game state query is a vanilla way to specify conditions for some content like shop data, inspired by Content Patcher's conditions. A query consists of a comma-delimited list of conditions in the form <type> [arguments], where <type> is case-sensitive. The type can be prefixed with ! to negate it. The query is true if it's null/blank, or if every listed condition exists and is true. For example, !SEASON Spring, WEATHER Here sunny is true on sunny non-spring days.

Conditions

World
Condition effect
DAY_OF_MONTH <day> Check the day of month.
DAY_OF_WEEK <day> Check the day of week, formatted as an integer between 0 (Sunday) through 6 (Saturday).
FARM_CAVE <type> The current farm cave (one of Bats, Mushrooms, or None).
FARM_NAME <name> Check the name of the farm.

Note: this only works for farm names that don't contain spaces.

FARM_TYPE <type> Check the farm type. The <type> is one of 1 (standard), 2 (riverland), 3 (forest), 4 (hilltop), 4 (combat), 5 (four corners), 6 (beach), or the ID for a custom farm type.
IS_HOST Check whether the current player is the main/host player.
IS_CUSTOM_FARM_TYPE Check whether the farm type is a custom one created by a mod. (This returns false for mods which edit/replace a vanilla farm type.)
SEASON <season> Check the season (one of spring, summer, fall, or winter).
LOCATION_ACCESSIBLE <name> Check whether the given location is accessible (one of CommunityCenter, JojaMart, or Railroad). Returns true for any other name, regardless of whether it's accessible.
WEATHER <location> <weather> Check whether the weather in the given location (one of Here or an internal location name) is Rainy or Sunny.
WORLD_STATE <id> Check whether any world state flag with the given <id> is set.
YEAR Check if the year is equal or more than the given value. For example, YEAR 2 is true in year 2 and all later years.
Player info & progress
Condition effect
PLAYER_COMBAT_LEVEL <player> <level>
PLAYER_FARMING_LEVEL <player> <level>
PLAYER_FISHING_LEVEL <player> <level>
PLAYER_FORAGING_LEVEL <player> <level>
PLAYER_MINING_LEVEL <player> <level>
Check the skill levels for the specified player(s).
PLAYER_CURRENT_MONEY <player> <amount> Check whether the specified player(s) currently have at least <amount> gold.
PLAYER_FARMHOUSE_UPGRADE <player> <level> Check whether the specified player(s) have upgraded their farmhouse or cabin to at least the given level (see possible levels).
PLAYER_GENDER <player> <gender> Check whether the specified player(s) are Male or Female.
PLAYER_HAS_CAUGHT_FISH <player> <id> Check whether the specified player(s) have caught at least one fish with the given ID.
PLAYER_HAS_CONVERSATION_TOPIC <player> <id> Check whether the specified player(s) have a conversation topic with the ID <id> active.
PLAYER_HAS_CRAFTING_RECIPE <player> <recipe name>
PLAYER_HAS_COOKING_RECIPE <player> <recipe name>
Check whether the specified player(s) know the crafting/cooking recipe identified by its internal name (spaces allowed). For example, PLAYER_HAS_CRAFTING_RECIPE CURRENT Field Snack.
PLAYER_HAS_DIALOGUE_ANSWER <player> <id> Check whether the specified player(s) have chosen the given dialogue answer in a previous dialogue.
PLAYER_HAS_FLAG <player> <id> Check whether the specified player(s) have the given mail flag set (with spaces allowed in the <id>).
PLAYER_HAS_ITEM <player> <item> Check whether the specified player(s) have at least one of a normal item (not bigcraftable, furniture, etc) in their inventory. The <item> can be 858 (Qi Gems), 73 (Walnuts), or the unqualified item ID.
PLAYER_HAS_ITEM_NAMED <player> <item name> Check whether the specified player(s) have at least one item in their inventory with the given <item name> (spaces allowed).
PLAYER_HAS_READ_LETTER <player> <id> Check whether the specified player(s) have read a letter, where <id> is the internal mail ID (spaces allowed). For example, PLAYER_HAS_READ_LETTER Any Visited_Island.
PLAYER_HAS_SECRET_NOTE <player> <id> Check whether the specified player(s) have read a secret note, where <id> is the secret note's integer ID.
PLAYER_HAS_SEEN_EVENT <player> <id> Check whether the specified player(s) have seen the event with given <id>.
PLAYER_MOD_DATA <player> <key> <value> Check whether the specified player(s) have a player.modData entry added by a mod with the given <key> and <value>.
PLAYER_MONEY_EARNED <player> <amount> Check whether the specified player(s) have earned at least <amount> gold.
Player relationships
Condition effect
PLAYER_HAS_CHILDREN <player> <count> Check whether the specified player(s) have least <count> children.
PLAYER_HAS_PET <player> Check whether the specified player(s) have a pet.
PLAYER_HEARTS <player> <npc> <heart level> Check whether the specified player(s) have a friend with at least <heart level> hearts of friendship. The <npc> can be an NPC's internal name, Any (check every NPC), or AnyDateable (check every romanceable NPC).
PLAYER_HAS_MET <player> <npc> Check whether the specified player(s) have talked to an NPC at least once. The <npc> is an NPC's internal name.
PLAYER_IS_DATING <player> <npc> Check whether the specified player(s) have given a bouquet to an NPC. The <npc> can be an NPC's internal name, or Any (check every romanceable NPC).
PLAYER_IS_DIVORCED <player> <npc> Check whether the specified player(s) are divorced. The <npc> can be an NPC's internal name or Any (with any NPC).
PLAYER_IS_ENGAGED <player> <target> Check whether the specified player(s) are engaged. The <target> can be an NPC's internal name, Any (with any NPC), or Player (with any player).
PLAYER_IS_MARRIED <player> <target> Check whether the specified player(s) are married. The <target> can be an NPC's internal name, Any (to any NPC), or Player (to any player).
PLAYER_IS_ROOMMATE <player> <target> Check whether the specified player(s) have a roommate. The <target> can be an NPC's internal name, Any (with any NPC), or Player (always false).
PLAYER_PREFERRED_PET <player> <pet> Check whether the preferred pet for the specified player(s) is Cat or Dog.
Save stats
Condition effect
DAYS_PLAYED <count> Check if at least <count> days have been played in the current save (including the current one).
MINE_LOWEST_LEVEL_REACHED <level> Check if any player has reached at least level <level> in the mines.
Randomization
Condition effect
RANDOM <value> Perform a random probability check. For example, RANDOM 0.4 is true 40% of the time. This recalculates the result each time it's called.
PICKED_VALUE_TICK <min> <min> <value> [seed offset]
PICKED_VALUE_DAYS <min> <max> <value> [seed offset]
Chooses a random value between <min> and <max>, and checks if it's equal to <value>. For example, PICKED_VALUE_TICK 1 10 2 has a 10% chance (check if 2 is equal to a random number between 1 and 10).

This always choose the same value for the current tick (if PICKED_VALUE_TICK) or in-game day (if PICKED_VALUE_DAYS). To have a different synchronized value, specify [seed offset] with an offset number (e.g. three options in a list would use offset 0, 1, and 2).

PICKED_VALUE <min> <max> <value> Not recommended for content packs, except in shop dialogues.
Equivalent to PICKED_VALUE_TICK, but checks the value selected by calling the GameStateQuery.PickRandomValue(random) method in code. The game only calls this method when opening a shop menu for shop data with dialogue.

Player ID

Some conditions have a <player> argument. This can be one of...

value result
Any At least one player must match the condition, regardless of whether they're online.
All Every player must match the condition, regardless of whether they're online.
Current The local player.
Host The main player.
Target In Data/WildTrees for the AdditionalChopDrops field only, the last player who chopped the tree. Otherwise equivalent to Current.
any other The unique multiplayer ID for the player to check.

Using queries elsewhere

C# code can use the GameStateQuery class to work with queries, like GameStateQuery.CheckConditions(query).

You can also use game state queries in event preconditions using the new G condition flag, like some_event_id/G !SEASON Spring, WEATHER Here sunny.

Extensibility

C# mods can define custom conditions by calling GameStateQuery.RegisterQueryType("condition name", (string[] fields) => ...). To avoid conflicts, prefixing custom condition names with your mod ID (like Example.ModId_SomeCondition) is strongly recommended.

Tokenizable string format

Stardew Valley 1.6 adds support for literal strings that contain tokens (currently only supported for custom map areas and custom shops).

Literal text

Previously you often needed to load text from a string key in data assets. With this new format, you can use the literal text directly in the asset instead (including Content Patcher tokens):

// before
"Dialogue": "Strings\\StringsFromCSFiles:ShopMenu.cs.11488",

// after: literal text supported
"Dialogue": "Welcome to Pierre's!",

// after: literal text with Content Patcher tokens
"Dialogue": "Welcome to Pierre's, {{PlayerName}}! {{i18n: translatable-text}}",

Tokens

You can inject tokens using square brackets. For example, "[LocalizedText Strings\StringsFromCSFiles:ShopMenu.cs.11488] This is raw text" will show something like "Welcome to Pierre's! This is raw text". Here are the supported tokens:

token format output
[ArticleFor <word>] The grammatical article (a or an) for the given word when playing in English, else blank. For example, [ArticleFor apple] apple will output an apple.
[FarmName] The farm name for the current save (without the injected "Farm" text).
[LocalizedText <string key>] Translation text loaded from the given string key. If the translation has placeholder tokens like {0}, you can add the values after the string key.
[PositiveAdjective] A random adjective from the Strings\Lexicon data asset's RandomPositiveAdjective_PlaceOrEvent entry.
[SuggestedItem] (For shops only.) The name of a random item currently available in the shop stock.

When passing an input argument for tokens like ArticleFor, the input can contain its own nested tokens. For example, "[ArticleFor [SuggestedItem]] [SuggestedItem]" would output something like an Apple.

If you're using Content Patcher, you can use its tokens anywhere in the string (including within square brackets); they'll be expanded before the game parses the string. For example, "{{Spouse}} would love [ArticleFor [SuggestedItem]] [SuggestedItem]!" would output something like "Alexa would love an Apple!".

Other changes

  • Added a display name field in English too for Data/Boots, Data/CookingRecipes, Data/CraftingRecipes, and Data/Furniture.
  • Added optional Conditions game state query field to Data/SpecialOrders.
  • Dropped support for non-string map & tile properties. Properties set to bool/int/float will be converted to string when the game loads them.

XNB impact

Here's a summary of the XNB files which changed in Stardew Valley 1.6.

This doesn't include...

  • changes in non-English files;
  • new fields when the vanilla entries didn't change (e.g. optional custom item fields).

Shorthand:

  • 'broken' means removing new content or potentially important changes, or potentially causing significant display bugs. This is a broad category — the game may work fine without it or crash, depending how it uses that specific content.
  • 'mostly unaffected' means mods will only be affected if they edit specific entries or fields.
  • Blank means no expected impact for the vast majority of mods.

XNB mods are disproportionately affected since (a) they replace the entire file and (b) they're loaded through the MonoGame content pipeline which is less tolerant of format changes.

content file changes XNB Content Patcher
Data/AquariumFish changed key type ✘ broken
Data/BigCraftablesInformation changed key type, standardized fields ✘ broken ✘ likely broken
Data/Boots changed key type, added display name ✘ broken ✘ likely broken
Data/ClothingInformation changed key type, standardized fields ✘ broken ✘ likely broken
Data/CookingRecipes added display name ✘ broken ✘ likely broken
Data/CraftingRecipes added display name ✘ broken ✘ likely broken
Data/Crops changed key type ✘ broken
Data/Events/IslandSouth fixed typo ✘ will remove changes ✓ mostly unaffected
Data/Fish changed key type ✘ broken
Data/FishPondData changed ItemID field type ✘ broken
Data/fruitTrees changed key type ✘ broken
Data/Furniture changed key type, standardized fields, added display name ✘ broken ✘ likely broken
Data/hats changed key type, standardized fields, added display name ✘ broken ✘ likely broken
Data/IngameMap new
Data/ObjectInformation changed key type, standardized fields ✘ broken ✘ likely broken
Data/Shops new
Data/ToolData new
Data/weapons changed key type, added display name ✘ broken ✘ likely broken
Data/WildTrees new
Maps/AbandonedJojaMart
Maps/Backwoods
Maps/Backwoods_GraveSite
Maps/Backwoods_Staircase
Maps/Barn
Maps/Barn2
Maps/Barn3
Maps/BoatTunnel
Maps/Cabin
Maps/Cabin1
Maps/Cabin1_marriage
Maps/Cabin2
Maps/Cabin2_marriage
Maps/Coop
Maps/Coop2
Maps/Coop3
Maps/Farm
Maps/Farm_Combat
Maps/Farm_Fishing
Maps/Farm_Foraging
Maps/Farm_FourCorners
Maps/Farm_Greenhouse_Dirt
Maps/Farm_Greenhouse_Dirt_FourCorners
Maps/Farm_Island
Maps/Farm_Mining
Maps/FarmHouse
Maps/FarmHouse_Bedroom_Normal
Maps/FarmHouse_Bedroom_Open
Maps/FarmHouse_Cellar
Maps/FarmHouse_ChildBed_0
Maps/FarmHouse_ChildBed_1
Maps/FarmHouse_CornerRoom_Add
Maps/FarmHouse_CornerRoom_Remove
Maps/FarmHouse_SouthernRoom_Add
Maps/FarmHouse_SouthernRoom_Remove
Maps/FarmHouse1
Maps/FarmHouse1_marriage
Maps/FarmHouse2
Maps/FarmHouse2_marriage
Maps/Greenhouse
Maps/IslandFarmHouse
Maps/MarnieBarn
Maps/Mine
Maps/Mountain
Maps/MovieTheater
Maps/MovieTheaterScreen
Maps/spousePatios
Maps/Sunroom
Maps/Mines/Volcano_SetPieces_32
changed map & tile properties to string
Maps/paths added icon for custom wild tree spawn

See also