Modding:Migrate to Stardew Valley 1.6.9

From Stardew Valley Wiki
Revision as of 03:42, 26 July 2024 by Pathoschild (talk | contribs) (→‎New utility fields & methods: clarify this list isn't exhaustive)
Jump to navigation Jump to search

Index

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


This page explains how to update your mods for compatibility with Stardew Valley 1.6.9, and documents some of the changes and new functionality. See also Migrate to Stardew Valley 1.6.

FAQs

What's changing?

Stardew Valley 1.6.9 contains many smaller changes for mods after the major 1.6 update.

Is this the modapocalypse?

No. The vast majority of mods should be unaffected by the changes in 1.6.9, and SMAPI will automatically rewrite most mods that are.

How to update your mod

Most mods won't need any changes. Here's a suggested 'quick start' guide to update a mod for Stardew Valley 1.6.9.

For C# mods:
  1. If you haven't already, update the mod for Stardew Valley 1.6.0.
  2. Rebuild the solution.
  3. Fix any build errors. You can search this page for relevant info as needed.
  4. Test to make sure the mod works.
  5. Skim through the changes below, and check any section that might be relevant to the mod.
For Content Patcher packs:
  1. If you haven't already, follow the Content Patcher migration guide until your content pack has "Format": "2.3.0".
    Do not skip this step! Content Patcher will assume your content pack is pre-1.6 if you don't, which can cause confusing errors if you already updated it.
  2. Skim through the changes below, and check any section that might be relevant to the mod.
For content packs which use another framework:
See the documentation for the framework mod. Often you won't need to update the content pack if the framework mod itself was updated.

Changes for all mods

Full list of changes

See the 1.6.9 release notes for a full list of technical changes. This page focuses on documentation for larger changes which may affect mods or mod authors.

Dialogue changes

  • 1.6.9 adds several new dialogue keys:
    asset key format description
    Characters/Dialogue/<name> AcceptBirthdayGift_<taste>_<context tag> (Optional) Shown when the NPC receives a birthday gift with the specified gift taste and context tag. See AcceptGift_<taste> for valid gift tastes.

    This is checked immediately before the AcceptBirthdayGift_<context tag> key added in 1.6.

    AcceptGift_<taste>_<context tag> (Optional) Shown when the NPC receives a non-birthday gift with the specified gift taste and context tag. See AcceptGift_<taste> for valid gift tastes.

    This is checked immediately before the AcceptGift_<context tag> key added in 1.6.

    AcceptGift_<taste> (Optional) Shown when the NPC receives a gift with the specified gift taste. The <taste> can be a specific taste level (Loved, Liked, Neutral, Disliked, or Hated), Positive (neutral/liked/loved), or Negative (disliked/hated).

    This is checked after all AcceptGift_* keys added in 1.6.

    AcceptGift (Optional) Shown when the NPC receives a non-birthday gift, if no other AcceptGift_* dialogue key was found.
    RejectBouquet_AlreadyAccepted_Engaged
    RejectBouquet_AlreadyAccepted_Married
    RejectBouquet_AlreadyAccepted
    (Optional) Shown when the player gives a bouquet to an NPC who already accepted one from them.
    RejectMermaidPendant_AlreadyAccepted_Engaged
    RejectMermaidPendant_AlreadyAccepted_Married
    RejectMermaidPendant_AlreadyAccepted
    (Optional) Shown when the player gives a mermaid's pendant to an NPC who already accepted one from them.
    RejectRoommateProposal_AlreadyAccepted
    RejectRoommateProposal_NpcWithSomeoneElse
    RejectRoommateProposal_PlayerWithSomeoneElse
    RejectRoommateProposal_LowFriendship
    RejectRoommateProposal_SmallHouse
    (Optional) Shown when the NPC rejects a roommate proposal because the player doesn't meet a specific requirement:
    • AlreadyAccepted: the NPC is already a roommate with this player.
    • NpcWithSomeoneElse: the NPC is already a roommate with another player.
    • PlayerWithSomeoneElse: the player making the proposal already has a roommate.
    • LowFriendship: the player doesn't have 10+ hearts with the NPC.
    • SmallHouse: the player hasn't upgraded their house yet.
    RejectRoommateProposal (Optional) Shown when the NPC rejects a roommate proposal, if a more specific RejectRoommateProposal_* dialogue wasn't found.
  • When using the $y dialogue command, you can now escape asterisks. For example, A*B will split A and B into separate dialogue boxes like before, but A**B will be shown with a single literal asterisk.

Game state query changes

1.6.9 adds one new game state query:

Condition effect
DATE_RANGE <min season> <min day> <min year> [max season] [max day] [max year] Whether the calendar date is within the specified range, inclusively. The max values default to winter (season), 28 (day), and unlimited (year) if omitted.

For example, between summer 15 and winter 15 in year one:

DATE_RANGE Summer 15 1 Winter 15 1

Or fall 15 or later:

DATE_RANGE Fall 15 1
PLAYER_HAS_TRINKET <player>+ <trinket ID>+ Whether the specified player(s) have one of the listed trinkets equipped. Each ID can be a qualified or unqualified item ID.

Debug command changes

1.6.9 adds two debug command:

command description
listLights Show debug info about all currently active light sources.
worldMapPosition [includeLog] Show detailed info to help troubleshoot world map positioning data. If [includeLog] is true, it will print a detailed log of how the current position was determined based on the Data/WorldMap entries.

These have been removed:

command notes
buildCoop Replaced by debug build coop.

And other debug command changes:

command changes
build The building type is now case-insensitive, and will list fuzzy matches if an exact match isn't found.
event The dontClearEvent option (default false) has been replaced by clearEventsSeen (default true), and no longer treats any value as true.
furniture Now allows non-numeric furniture IDs.
item The item ID argument is now required.
mineLevel Added an optional [layout] argument, which can be set to the layout number in Content/Maps/Mines. For example, debug mineLevel 101 47 warps to mine level 101 using layout 47.
setUpFarm The clearMore argument no longer treats any value as true.
warpCharacterTo Added a separate argument for the NPC's facing direction, instead of using the Y argument.
various Fixed debug commands for crops not affecting those in garden pots. This affects debug growCrops, spreadSeeds, and water.

Translation changes

  • Many of the 1.6 translations were moved from Strings/1_6_Strings into Strings/BigCraftables, Strings/Objects, and Strings/Tools.
  • Tool names/descriptions were moved from Strings/StringsFromCSFiles into a new Strings/Tools asset. Dynamic tool names (like "Gold {0}") have also been replaced by specific names (like "Gold Axe") to allow fixing awkward translations.

Building data changes

1.6.9 adds a new field in Data/Buildings, for indoor item entries:

field effect
ClearTile (Optional) Whether to remove any item on the target tile, except for one matching the indoor item's ItemId. The previous contents of the tile will be moved into the lost and found if applicable. Default true.

Farm animal data changes

1.6.9 adds a new field in Data/FarmAnimals:

field effect
Shadow (Optional) The shadow to draw under the farm animal, if a more specific field like ShadowWhenAdult doesn't apply. Defaults to omitted.

Item data changes

1.6.9 adds a new field in Data/Objects:

field effect
ColorOverlayFromNextIndex (Optional) When drawn as a colored object, whether to apply the color to the next sprite in the spritesheet and draw that over the main sprite. If false, the color is applied to the main sprite instead. Default false.

And for all item spawn fields:

field effect
Color (Optional) The tint color to apply to the produced item. Default none.

Machine data changes

1.6.9 fixes some issues with Data/Machines:

  • The PreserveId field now supports the DROP_IN_PRESERVE token.
  • The CopyColor field now works for any input item, even if it's not a ColoredObject (in which case it uses the color from its context tags).
  • The CopyQuality field no longer disables QualityModifiers; those are now applied to the result of the copy.

Pet data changes

In Data/Pets, the Gifts field now uses item spawn fields (including conditions and item quality/color/name/etc). The previous QualifiedItemID and Stack fields are now deprecated, but content packs which set them should still work.

Tool data changes

In Data/Tools, the ApplyUpgradeLevelToDisplayName field no longer exists. Every tool now has its own name instead (see translation changes).

Trigger action changes

1.6.9 adds a new field in Data/TriggerActions:

field effect
SkipPermanentlyCondition (Optional) If set, a game state query which indicates that the action should be marked applied when this condition matches. This happens before Condition, Action, and Actions are applied.

This mainly allows optimizing cases where the action will never be applied, to avoid parsing the Condition each time.

Changes for C# mods

'Get player' changes

The Game1.getFarmer and Game1.getFarmerMaybeOffline methods are now obsolete and should no longer be used. They've been replaced by a unified Game1.GetPlayer method.

The old methods have inconsistent and sometimes counterintuitive behavior. For example, getFarmer(id) returns the main player if the target player is offline, which usually isn't the expected result. getFarmer also returns the main player given an invalid player ID, while getFarmerMaybeOffline returns null in that case. The new method matches offline players by default, returns null if the ID isn't found (so you can choose the best fallback behavior), and has full code docs and nullability annotations.

To migrate existing code:

old code migration
Farmer player = Game1.getFarmer(id);
The exact equivalent is:
Farmer player = Game1.GetPlayer(id, onlineOnly: true) ?? Game1.MasterPlayer;

However, most mods probably didn't expect it to work that way. Consider whether you meant this instead:

Farmer? player = Game1.GetPlayer(id);
Farmer? player = Game1.getFarmerMaybeOffline(id);
This is directly equivalent to:
Farmer? player = Game1.GetPlayer(id);

Case-insensitive string comparisons

1.6.9 adds several extension methods in the StardewValley.Extensions namespace for comparing strings case-insensitively. Specifically: ContainsIgnoreCase, EqualsIgnoreCase, IndexOfIgnoreCase, StartsWithIgnoreCase, and EndsWithIgnoreCase.

For example:

// before 1.6.9
if (string.Equals(left, right, StringComparison.OrdinalIgnoreCase))
   ...

// after 1.6.9
if (left.EqualsIgnoreCase(right))
   ...

The new methods also handle null as you'd expect. For example, left.EqualsIgnoreCase(right) will be true if both are null.

Screen reader changes

1.6.9 adds fields to support screen reader mods. These let mods with custom UI elements add information which can be shown to blind players, without requiring an integration with each screen reader mod.

The main change is a new interface:

/// <summary>A UI element that provides information for screen readers.</summary>
/// <remarks>These values aren't displayed by the game; they're provided to allow for implementing screen reader mods.</remarks>
public interface IScreenReadable
{
    /// <summary>If set, the translated text which represents this component for a screen reader. This may be the displayed text (for a text component), or an equivalent representation (e.g. "exit" for an 'X' button).</summary>
    string ScreenReaderText { get; }

    /// <summary>If set, a translated tooltip-like description for this component which can be displayed by screen readers, in addition to the <see cref="ScreenReaderText"/>.</summary>
    string ScreenReaderDescription { get; }

    /// <summary>Whether this is a purely visual component which should be ignored by screen readers.</summary>
    bool ScreenReaderIgnore { get; }
}

This is implemented by ClickableComponent and OptionsElement, so you can add screen reader data to most UI elements out of the box.

Light source revamp

1.6.9 revamps how light sources are tracked:

  • LightSource now has a required string ID (LightSource.Id), instead of the former optional numeric ID (LightSource.Identifier).
  • Game1.currentLightSources is now a dictionary by ID. (You can still write Game1.currentLightSources.Add(light) like before if you add using StardewValley.Extensions.)
  • Added unique IDs for every vanilla light source.
  • Added validation warnings and auto-recovery for some issues.
  • Added null handling in related helpers. For example, you can replace logic like if (light != null) Utility.repositionLightSource(light.Id, position) with Utility.repositionLightSource(light?.Id, position).
  • Added optional LightSource.onlyLocation field, which ensures that the light is only shown when viewing that location.
  • Added code docs.
  • Removed the TemporaryAnimatedSprite.light boolean field; the light is now enabled by setting the lightId field.

New utility fields & methods

1.6.9 adds many new utility methods and fields. For example:

member usage
object dictionariesGetValueOrDefault(…) Object dictionaries (like GameLocation.objects) now implement the .NET GetValueOrDefault method, which lets you simplify common code:
// before 1.6.9
string? itemId = location.objects.TryGetValue(tile, out Object? obj)
    ? obj.ItemId
    : null;

// after 1.6.9
string? itemId = location.objects.GetValueOrDefault(tile)?.ItemId;
Bush.readyForHarvest() Get whether this bush has berries or fruit to harvest.
ClickableTextureComponent.drawLabel Set whether the label should be drawn. This lets you add a label that's not rendered automatically.
ColoredObject.TrySetColor(…) Apply a tint color to an arbitrary item. This will change the input item directly if it's a ColoredObject, otherwise it'll create a new ColoredObject instance.

For example:

Item inputItem = ItemRegistry.Create("(O)128"); // pufferfish
if (ColoredObject.TrySetColor(inputItem, Color.Red, out ColoredObject? coloredObj)) // coloredObj = red pufferfish
   ...;
CrabPot.NeedsBait(player) Get whether the crab pot doesn't have bait, and bait is needed before it can catch anything based on the player's professions.
Crop.replaceWithObjectOnFullGrown If set, the qualified object ID to spawn on the crop's tile when it's full-grown. The crop will be removed when the object is spawned.
GameLocation.ForEachDirt(…) Perform an action for every tilled dirt in the location. You can optionally exclude dirt in garden pots.

For example:

Game1.currentLocation.ForEachDirt(dirt =>
{
    dirt.Pot?.Water();
    dirt.state.Value = HoeDirt.watered;
    return true;
});
HoeDirt.Pot The garden pot which holds the dirt, if applicable. This is set automatically by the game.
Item.BaseName If the item's Name contains temporary dynamic changes like the " Recipe" suffix for a recipe, the item name without those changes.
Item.CopyFieldsFrom(otherItem) Copy all fields from another item into the current one. This is equivalent to item.getOne(), except that you update an existing item instead.
Item.LearnRecipe() Learn the recipe for this item (regardless of whether the recipe is in Data/CookingRecipes or Data/CraftingRecipes).
MineShaft.IsGeneratedLevel(…)
VolcanoDungeon.IsGeneratedLevel(…)
These methods already existed before 1.6.9, but they now have overloads without the out parameters to simplify common logic. For example:
if (MineShaft.IsGeneratedLevel(location))
   ...;
Object.GetPreservedItemId() Get the item ID which was preserved as part of this item (e.g. the tulip ID for tulip honey), if applicable. This is equivalent to item.preservedParentSheetIndex, except in special cases like wild honey where preservedParentSheetIndex is set to a non-item value like -1.
Raccoon.GetBundle()
Raccoon.GetBundle(timesFed)
Get the raccoon bundle that will be shown currently, or after completing a given number of bundles.
Utility.fuzzySearchAll(…) Find all matches for a search term based on fuzzy compare rules.

For example:

IEnumerable<string> coopNames = Utility.FuzzySearchAll("coop", Game1.buildingData.Keys); // [ "Coop", "Big Coop", "Deluxe Coop" ]
WorldDate.GetDaysPlayed(…) Get the total days for a given date from the start of the game, to allow for efficient date comparisons.

For example:

int minDaysPlayed = WorldDate.GetDaysPlayed(year, season, dayOfMonth);
int actualDaysPlayed = Game1.Date.TotalDays;
if (actualDaysPlayed >= minDaysPlayed)
   ...;

New constants

1.6.9 adds new constants:

constant usage
AchievementIds.* The IDs for achievements in Data/Achievements. For example:
// before 1.6.9
if (player.achievements.Contains(34))
   ...;

// after 1.6.9
if (player.achievements.Contains(AchievementIds.FullShipment))
   ...;
Item.ErrorItemName The "Error Item" name shown for invalid items.
SpecialCurrencyDisplay.currency_qiGems
SpecialCurrencyDisplay.currency_walnuts
The currency types supported by the special currency display (shown in the top-left corner when you find a Qi gem or golden walnut).

OutputMethod changes in Data/Machines

If you have custom C# methods which are used in the OutputMethod field in Data/Machines, the method signature has changed in 1.6.9. You'll need to add the new Farmer player argument to your methods:

/// <summary>The method signature for a custom <see cref="MachineItemOutput.OutputMethod"/> method.</summary>
/// <param name="machine">The machine instance for which to produce output.</param>
/// <param name="inputItem">The item being dropped into the machine, if applicable.</param>
/// <param name="probe">Whether the machine is only checking the output that would be produced. If so, the input/machine shouldn't be changed and no animations/sounds should play.</param>
/// <param name="outputData">The item output data from <c>Data/Machines</c> for which output is being created, if applicable.</param>
/// <param name="player">The player interacting with the machine, if any.</param>
/// <param name="overrideMinutesUntilReady">The in-game minutes until the item will be ready to collect, if set. This overrides the equivalent fields in the machine data if set.</param>
/// <returns>Returns the item to produce, or <c>null</c> if none should be produced.</returns>
public delegate Item MachineOutputDelegate(Object machine, Item inputItem, bool probe, MachineItemOutput outputData, Farmer player, out int? overrideMinutesUntilReady);

Changes for SMAPI and external tools

Content hashes

The game now has a Content/ContentHashes.json file, which contains the MD5 hash for every vanilla content asset. This lets SMAPI, mod managers, and other tools auto-detect content files that are outdated, broken, missing, or replaced by XNB mods.