Modding:Migrate to Stardew Valley 1.6

From Stardew Valley Wiki
Revision as of 05:13, 20 January 2022 by Spotteddotted (talk | contribs) (grammar edit)
Jump to navigation Jump to search

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 (and sometimes QualifiedItemID) 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"

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.

Fruit trees

Fruit trees are a bit different since they have three separate entities (the sapling item, the fruit tree itself, and the fruit item it produces). Note that the key in Data/FruitTrees is the sapling's item ID, and the value references the fruit's item ID.

{
    "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": "0/spring/Example.ModId_Pufferfruit/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
        },
    ]
}

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.

Custom map areas

The valley map, with one map area highlighted.

You can now add/edit areas on the game menu's map UI 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:

index field effect
0 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.
1 Include when
detecting group
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.
2 Texture The texture asset name to draw when the area is applied to the map (using \ in the path, or \\ in JSON).
3 Source rectangle The pixel area within the texture asset name to draw, or -1 to draw the entire texture.
4 Target rectangle The pixel area within the map onto which to apply the area if it matches.
5 Condition A game state query which checks whether the area should be applied, or an empty string to always apply.
6 Show ??? Whether to replace the tooltip for the area with ??? if the condition isn't met.
7 Tooltip The string key to load for the tooltip shown when the mouse is over the area, and the scroll at the bottom of the map when the player is in the location. This canbe empty to disable the tooltip/scroll.
8 In-game locations The in-game locations to match with the map. These are used to figure out (a) which map group a player is in, and (b) where they should be shown on the map given their in-game position.

This consists of sequences of these 10 fields for each location:

index field effect
0 Locations A comma-delimited list of location names. Each location can be one of...
  • An internal location name (as shown by the Debug Mode mod), without spaces. 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 Desert,SkullCave,SandyHouse,SandyShop,Club.
  • CONTEXT_Default for any location in the valley.
  • CONTEXT_Island for any location on Ginger Island.
1–4
5–8
tile corners
map corners
The corner coordinates for the in-game location measured in tiles (in the form <top-left x> <bottom-right x> <top-left y> <bottom-right y>), and for the equivalent map area measured in pixels (in 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 the positions are set to -1, the player marker is simply placed in the center of the map area.

9 Scroll override A string key for the scroll text to show at the bottom of the map when the player is in this area, or the text null to use the tooltip for the map area (if any). If this is the exact string FarmName, it shows the farm name instead.

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 in the dialogue format.

Translations can be loaded from a data asset by surrounding the key with square brackets. For example, "[Strings\StringsFromCSFiles:ShopMenu.cs.11488]This is raw text" will show something like "Welcome to Pierre's! This is raw text". If the translation text contains placeholder tokens like {0}, you can add a comma-delimited list of substitution values within the square brackets. Each append can be one of SuggestedItem (the name of a random item in the shop stock), PositiveAdjective (a random adjective from Strings\Lexicon:RandomPositiveAdjective_PlaceOrEvent), ArticleFor <word or append code> (a or an depending on the word in English, else blank), or a literal string value. For example, "[Strings\StringsFromCSFiles:ShopMenu.cs.11464,SuggestedItem,PositiveWord,ArticleFor SuggestedItem]" will produce something like "I've got an Apple that would look just wonderful in your house."

If you're using Content Patcher and don't need the game's substitution codes like SuggestedItem, you can also use your own translations directly with its i18n token instead.

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

See custom items above, which covers fruit trees too.

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.

Exception: for item data assets, the sprite index and texture name fields are optional and can 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.
TICK_CHOOSE <min> <min> <value> [seed offset] Chooses a random value between <min> and <max> using the game's update tick count as a seed, and checks if it's equal to <value>. For example, TICK_CHOOSE 1 10 2 has a 10% chance (check if 2 is equal to a random number between 1 and 10). All TICK_CHOOSE checks within the same tick are synchronized, so this can be used for logic like choosing only one of three options in one list. The [seed offset] value (if specified) is just added to the tick count, for cases where you need to choose a different value on the same tick.
PICKED_VALUE <min> <max> <value> Not recommended for content packs, except in shop dialogues.
Equivalent to TICK_CHOOSE, 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...

  • Any (at least one player, regardless of whether they're online);
  • All (every player, regardless of whether they're online);
  • Current (the local player);
  • Host (the main player);
  • or 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.

XNB impact

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

Notes:

  • This ignores text changes in non-English files for simplicity.
  • New content files aren't listed, since they won't impact existing mods.
  • XNB mods are disproportionately affected, since they replace the entire file. However Content Patcher packs are significantly affected by this update due to Standardized data 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.
content file changes XNB Content Patcher
TODO

See also