Difference between revisions of "Modding:Trigger actions"

From Stardew Valley Wiki
Jump to navigation Jump to search
(→‎Elsewhere: fix typo, tweak capitalization for consistency)
m (→‎Extensibility: Tweak code example (two "out string error" lines will produce a compiler error))
 
(16 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{upcoming|1.6}}
 
 
 
← [[Modding:Index|Index]]
 
← [[Modding:Index|Index]]
  
Line 26: Line 24:
 
! action
 
! action
 
! effect
 
! effect
 +
|-
 +
| <samp>AddBuff {{t|buff ID}}</samp><br /><samp>RemoveBuff {{t|buff ID}}</samp>
 +
| Apply or remove a buff ID for the current player.
 
|-
 
|-
 
| <samp>AddConversationTopic {{t|topic ID}} {{t|day duration}}</samp>
 
| <samp>AddConversationTopic {{t|topic ID}} {{t|day duration}}</samp>
Line 32: Line 33:
 
| <samp>RemoveConversationTopic {{t|topic ID}}</samp>
 
| <samp>RemoveConversationTopic {{t|topic ID}}</samp>
 
| End a [[Modding:Dialogue#Conversation topics|conversation topic]], if it's active.
 
| End a [[Modding:Dialogue#Conversation topics|conversation topic]], if it's active.
|-
 
| <samp>AddCookingRecipe {{t|player}} {{t|recipe ID}}</samp><br /><samp>AddCraftingRecipe {{t|player}} {{t|recipe key}}</samp>
 
| Add a [[Modding:Recipe data|cooking or crafting recipe]] to the [[#Target player|specified player(s)]].
 
 
|-
 
|-
 
| <samp>AddFriendshipPoints {{t|NPC name}} {{t|count}}</samp>
 
| <samp>AddFriendshipPoints {{t|NPC name}} {{t|count}}</samp>
Line 77: Line 75:
 
| <samp>AddSpecialOrder {{t|order ID}}</samp><br /><samp>RemoveSpecialOrder {{t|order ID}}</samp>
 
| <samp>AddSpecialOrder {{t|order ID}}</samp><br /><samp>RemoveSpecialOrder {{t|order ID}}</samp>
 
| Add or remove a [[Modding:Special orders|special order]].
 
| Add or remove a [[Modding:Special orders|special order]].
 +
|-
 +
| <samp>If {{t|query}} ## {{t|action if true}}</samp><br /><samp>If {{t|query}} ## {{t|action if true}} ## {{t|action if false}}</samp>
 +
| Check a [[Modding:Game state queries|game state query]] and perform an action based on the result.
 +
 +
For example, this only sends a mail if the player doesn't have it in their received, mailbox, or queued-for-tomorrow mail:
 +
<syntaxhighlight lang="js">
 +
"ActionsOnPurchase": [
 +
    "If !PLAYER_HAS_MAIL Current SomeFlag ## AddMail Current SomeFlag"
 +
]
 +
</syntaxhighlight>
 +
|-
 +
| <samp>IncrementStat {{t|stat key}} {{o|amount}}</samp>
 +
| Increment a stat value by the given amount (default 1) for the current player. This can be a vanilla stat key (see the <samp>PLAYER_STAT</samp> [[Modding:Game state queries|game state query]] for a list) or a custom stat key. The amount can be negative to decrement it.
 
|-
 
|-
 
| <samp>MarkActionApplied {{t|player}} {{t|answer ID}} {{o|applied}}</samp>
 
| <samp>MarkActionApplied {{t|player}} {{t|answer ID}} {{o|applied}}</samp>
 
| Mark a <samp>Data/TriggerActions</samp> entry as applied or non-applied for the [[#Target player|specified player(s)]], depending on {{o|applied}} (default <samp>true</samp>). This can be used to skip or re-run an entry, since <samp>Data/TriggerActions</samp> entries are only applied once by default.
 
| Mark a <samp>Data/TriggerActions</samp> entry as applied or non-applied for the [[#Target player|specified player(s)]], depending on {{o|applied}} (default <samp>true</samp>). This can be used to skip or re-run an entry, since <samp>Data/TriggerActions</samp> entries are only applied once by default.
  
Note that an entry can't mark ''itself'' unapplied (but you can add a second entry which marks the first one unapplied).
+
Note that an entry can't use this to mark ''itself'' unapplied; see [[#Make Data/TriggerActions repeat|''Make <samp>Data/TriggerActions</samp> repeat'']] if you want to do that.
 +
|-
 +
| <samp>MarkCookingRecipeKnown {{t|player}} {{t|recipe ID}} {{o|known}}</samp><br /><samp>MarkCraftingRecipeKnown {{t|player}} {{t|recipe key}} {{o|known}}</samp>
 +
| Set whether [[#Target player|specified player(s)]] know a [[Modding:Recipe data|cooking or crafting recipe]], depending on {{o|known}} (default <samp>true</samp>).
 +
 
 +
Note that forgetting a recipe will also reset its times-cooked/crafted counter to zero.
 
|-
 
|-
 
| <samp>MarkEventSeen {{t|player}} {{t|event ID}} {{o|seen}}</samp>
 
| <samp>MarkEventSeen {{t|player}} {{t|event ID}} {{o|seen}}</samp>
Line 88: Line 104:
 
| <samp>MarkQuestionAnswered {{t|player}} {{t|answer ID}} {{o|answered}}</samp>
 
| <samp>MarkQuestionAnswered {{t|player}} {{t|answer ID}} {{o|answered}}</samp>
 
| Mark [[Modding:Dialogue#Response IDs|a dialogue answer]] as selected or non-selected for the [[#Target player|specified player(s)]], depending on {{o|answered}} (default <samp>true</samp>).
 
| Mark [[Modding:Dialogue#Response IDs|a dialogue answer]] as selected or non-selected for the [[#Target player|specified player(s)]], depending on {{o|answered}} (default <samp>true</samp>).
 +
|-
 +
| <samp>MarkSongHeard {{t|player}} {{t|song ID}} {{o|heard}}</samp>
 +
| Mark a song track's cue name heard or non-heard for the [[#Target player|specified player(s)]], depending on {{o|heard}} (default <samp>true</samp>). This affects whether the song appears in the [[jukebox]] selection.
 
|-
 
|-
 
| <samp>Null</samp>
 
| <samp>Null</samp>
 
| ''(Specialized)'' Does nothing. This is used internally; there's generally no benefit to using it yourself.
 
| ''(Specialized)'' Does nothing. This is used internally; there's generally no benefit to using it yourself.
 +
|-
 +
| <samp>RemoveTemporaryAnimatedSprites</samp>
 +
| Remove all temporary animated sprites in the current location. For example, this can be used in the [[Modding:Event data|event]] <samp>setSkipActions</samp> command to clean up the event's temporary sprites.
 
|-
 
|-
 
| <samp>SetNpcInvisible {{t|NPC name}} {{t|day duration}}</samp>
 
| <samp>SetNpcInvisible {{t|NPC name}} {{t|day duration}}</samp>
Line 160: Line 182:
 
|-
 
|-
 
| <samp>Id</samp>
 
| <samp>Id</samp>
| The [[Modding:Modder Guide/Game Fundamentals#Unique string IDs|unique string ID]] for this trigger action.
+
| The [[Modding:Common data field types#Unique string ID|unique string ID]] for this trigger action.
 
|-
 
|-
 
| <samp>Trigger</samp>
 
| <samp>Trigger</samp>
Line 190: Line 212:
  
 
This is just a shortcut for <samp>Actions</samp> with one action. Technically you can use both together, but usually you should just pick one property to set.
 
This is just a shortcut for <samp>Actions</samp> with one action. Technically you can use both together, but usually you should just pick one property to set.
|-
 
| <samp>Location</samp>
 
| ''(Optional)'' If set, the internal location name where this action should be applied. This is a shortcut for (and more efficient than) using a <samp>LOCATION_NAME</samp> game state query. Default none.
 
 
|-
 
|-
 
| <samp>HostOnly</samp>
 
| <samp>HostOnly</samp>
 
| ''(Optional)'' Whether this trigger action can only run for the main player. If true, the action will be ignored for farmhands in [[multiplayer]].
 
| ''(Optional)'' Whether this trigger action can only run for the main player. If true, the action will be ignored for farmhands in [[multiplayer]].
 +
|-
 +
| <samp>MarkActionApplied</samp>
 +
| ''(Optional)'' Whether to mark the action applied when it's applied. Default true.
 +
 +
* If true: the action is added to the player's <samp>triggerActionsRun</samp> list, [[Modding:Game state queries|queries]] like <samp>PLAYER_HAS_RUN_TRIGGER_ACTION</samp> will return true, and the action won't run again (unless you use the <samp>MarkActionApplied</samp> action to mark it unapplied).
 +
* If false: the action can repeat immediately when the same trigger is raised, and [[Modding:Game state queries|queries]] like <samp>PLAYER_HAS_RUN_TRIGGER_ACTION</samp> will return false for it.
 
|-
 
|-
 
| <samp>Condition</samp>
 
| <samp>Condition</samp>
Line 216: Line 241:
 
<syntaxhighlight lang="js">
 
<syntaxhighlight lang="js">
 
"{{ModId}}_Event": "continue/64 15/farmer 64 16 2 Abigail 64 18 0/pause 1500/speak Abigail \"Hi. Here's 10g and a parsnip.\"/action AddMoney 10/action AddItem (O)24/pause 500/end"
 
"{{ModId}}_Event": "continue/64 15/farmer 64 16 2 Abigail 64 18 0/pause 1500/speak Abigail \"Hi. Here's 10g and a parsnip.\"/action AddMoney 10/action AddItem (O)24/pause 500/end"
</syntaxhighlight></li>
+
</syntaxhighlight>
 +
 
 +
See also the <samp>setSkipActions</samp> command.
 +
</li>
  
 
<li>A [[Modding:Mail data|mail letter]] using the <samp>%action</samp> command. For example:
 
<li>A [[Modding:Mail data|mail letter]] using the <samp>%action</samp> command. For example:
Line 236: Line 264:
 
C# mods can use the <samp>StardewValley.Triggers.TriggerActionManager</samp> class to interact with trigger actions.
 
C# mods can use the <samp>StardewValley.Triggers.TriggerActionManager</samp> class to interact with trigger actions.
  
For example, you can add a new trigger type:
+
For example, you can...
 +
<ul>
 +
<li>Add and raise a new trigger type:
 
<syntaxhighlight lang="js">
 
<syntaxhighlight lang="js">
 
// register custom trigger type
 
// register custom trigger type
 
TriggerActionManager.RegisterTrigger("Some.ModId_OnItemReceived");
 
TriggerActionManager.RegisterTrigger("Some.ModId_OnItemReceived");
  
// run actions for the custom trigger
+
// run actions in Data/TriggerActions for the custom trigger
 
TriggerActionManager.Raise("Some.ModId_OnItemReceived", new[] { item, index }); // trigger can pass optional trigger arguments
 
TriggerActionManager.Raise("Some.ModId_OnItemReceived", new[] { item, index }); // trigger can pass optional trigger arguments
</syntaxhighlight>
+
</syntaxhighlight></li>
  
Or you can add a new action handler:
+
<li>Or add a new action:
 
<syntaxhighlight lang="js">
 
<syntaxhighlight lang="js">
 
TriggerActionManager.RegisterAction("Some.ModId_PlaySound", this.PlaySound);
 
TriggerActionManager.RegisterAction("Some.ModId_PlaySound", this.PlaySound);
Line 252: Line 282:
  
 
/// <inheritdoc cref="TriggerActionDelegate" />
 
/// <inheritdoc cref="TriggerActionDelegate" />
public static bool PlaySound(string[] args, string trigger, object[] triggerArgs, TriggerActionData data)
+
public static bool PlaySound(string[] args, TriggerActionContext context, out string error)
 
{
 
{
 
     // get args
 
     // get args
     if (!ArgUtility.TryGet(args, 1, out string soundId, out string error, allowBlank: false))
+
     if (!ArgUtility.TryGet(args, 1, out string soundId, out error, allowBlank: false))
         return TriggerActionManager.Helpers.LogActionError(args, trigger, data, error);
+
         return false;
  
 
     // apply
 
     // apply
Line 262: Line 292:
 
     return true;
 
     return true;
 
}
 
}
</syntaxhighlight>
+
</syntaxhighlight></li>
 +
 
 +
<li>Or run an action string:
 +
<syntaxhighlight lang="js">
 +
// NOTE: this is just an example of how to run an action. This is meant to support actions specified in data or content
 +
// packs. If you want to send mail (or perform other actions) in C#, it's better to call the C# APIs directly instead.
 +
string action = "AddMail Current Robin Now";
 +
if (!TriggerActionManager.TryRunAction(action, out string error, out Exception ex))
 +
    Game1.log.Error($"Failed running action '{action}': {error}", ex);
 +
</syntaxhighlight></li>
 +
</ul>
 +
 
 +
To avoid conflicts, custom trigger names should be [[Modding:Common data field types#Unique string ID|unique string IDs]].
 +
 
 +
==FAQs==
 +
===Trigger actions vs map actions===
 +
''Actions'' can refer to two different systems:
 +
 
 +
* ''Trigger actions'' (this page) let you perform generic background tasks that can be done anytime, like sending a letter or starting a quest. These can be triggered automatically based on conditions, or via commands in dialogue, events, etc.
 +
* ''[[Modding:Maps|Map actions]]'' refer to the <samp>Action</samp> and <samp>TouchAction</samp> map properties, which do something when you walk on or interact with a map tile. These can perform a wide array of map- and interaction-specific things like showing a message box, changing the map, opening shop menus, etc. These only work in maps, and generally don't make sense in other contexts.
 +
 
 +
Aside from the similar names, they're not interchangeable and there's fairly little overlap.
 +
 
 +
===Make <samp>Data/TriggerActions</samp> repeat===
 +
By default, each entry in <samp>Data/TriggerActions</samp> is only applied once per player.
 +
 
 +
There are two main ways to repeat actions:
 +
<ul>
 +
<li>To make it repeatable immediately, set <samp>"MarkActionApplied": false</samp> on the <samp>Data/TriggerActions</samp> entry.</li>
 +
<li>To enable repeating at a different time, you can use the <samp>MarkActionApplied</samp> action to forget that it was applied.
 +
 
 +
For example, this patch will set alternating 'work' or 'weekend' mail flags depending on the day of week:
 +
{{#tag:syntaxhighlight|<nowiki>
 +
{
 +
    "Format": "</nowiki>{{Content Patcher version}}<nowiki>",
 +
    "Changes": [
 +
        {
 +
            "Action": "EditData",
 +
            "Target": "Data/TriggerActions",
 +
            "Entries": {
 +
                // set 'work' flag on weekdays, and reset weekend action
 +
                "{{ModId}}_Work": {
 +
                    "Id": "{{ModId}}_Work",
 +
                    "Trigger": "DayStarted",
 +
                    "Condition": "!DAY_OF_WEEK Saturday Sunday",
 +
                    "Actions": [
 +
                        "AddMail Current {{ModId}}_Work Received",
 +
                        "RemoveMail Current {{ModId}}_Weekend",
 +
                        "MarkActionApplied Current {{ModId}}_Weekend false"
 +
                    ]
 +
                },
  
To avoid conflicts, custom trigger names should be [[Modding:Modder Guide/Game Fundamentals#Unique string IDs|unique string IDs]].
+
                // set 'weekend' flag on weekends, and reset work action
 +
                "{{ModId}}_Weekend": {
 +
                    "Id": "{{ModId}}_Weekend",
 +
                    "Trigger": "DayStarted",
 +
                    "Condition": "DAY_OF_WEEK Saturday Sunday",
 +
                    "Actions": [
 +
                        "AddMail Current {{ModId}}_Weekend Received",
 +
                        "RemoveMail Current {{ModId}}_Work",
 +
                        "MarkActionApplied Current {{ModId}}_Work false"
 +
                    ]
 +
                },
 +
            }
 +
        }
 +
    ]
 +
}
 +
</nowiki>|lang=js}}
  
 
[[Category:Modding]]
 
[[Category:Modding]]

Latest revision as of 23:54, 14 May 2024

Index

This page documents trigger actions, which let content packs perform an action when something happens. (C# mods should usually use SMAPI events instead.)

Overview

Introduction

A trigger action consists of two main parts:

  • The trigger is what causes the action to happen. This can be an entry in Data/TriggerActions, an event command, etc. See built-in triggers.
  • The action is a space-delimited string which defines what to do. For example, AddMail Current Robin adds the Robin letter to the player's mailbox tomorrow. See argument format and built-in actions.

Argument format

Arguments are space-delimited. For example, AddMail Current Abigail_LeoMoved Now calls the AddMail action with three arguments (player: Current, mail ID: Abigail_LeoMoved, and mail type: Now).

If you have spaces within an argument, you can surround it with quotes to keep it together. For example, AddFriendshipPoints "Mister Qi" 10 has two arguments (Mister Qi and 10). You can escape inner quotes with backslashes, like AddFriendshipPoints "Mister \"Qi\"" 10.

Remember that quotes and backslashes inside JSON strings need to be escaped too. For example, "AddFriendshipPoints \"Mister Qi\" 10" will send AddFriendshipPoints "Mister Qi" 10 to the game code. Alternatively, you can use single-quotes for the JSON string instead, like 'AddFriendshipPoints "Mister Qi" 10'.

Actions

Built-in actions

These are the built-in actions which can be used by any trigger. (Other custom actions may be added by C# mods.)

action effect
AddBuff <buff ID>
RemoveBuff <buff ID>
Apply or remove a buff ID for the current player.
AddConversationTopic <topic ID> <day duration> Start a conversation topic for the given number of days. If the topic is already active, this resets its duration to the given number.
RemoveConversationTopic <topic ID> End a conversation topic, if it's active.
AddFriendshipPoints <NPC name> <count> Add <count> friendship points between the current player and specified NPC. The <count> can be negative to reduce friendship points.
AddItem <item ID> [count] [quality] Add an item by its qualified or unqualified item ID to the current player's inventory, with an optional count (default 1) and quality (default 0).
RemoveItem <item ID> [count] Deduct items by qualified or unqualified item ID from the current player's inventory, up to a max combined stack size of [count] (default 1).
AddMail <player> <mail ID> [type]
RemoveMail <player> <mail ID> [type]
Add or remove a mail flag or letter for the specified player(s).

The <type> must be one of:

type effect
now In the player's mailbox now.
tomorrow In the player's list of mail arriving in the mailbox tomorrow.
received In the player's list of received mail (bypassing the mailbox).
all Add or remove it everywhere (mailbox, tomorrow's mailbox, and received mail).

If omitted, the <type> defaults to tomorrow for AddMail and all for RemoveMail.

AddMoney <amount> Add the given <amount> of money for the current player. The <amount> can be negative to deduct money.
AddQuest <quest ID>
RemoveQuest <quest ID>
Add or remove a quest for the current player.
AddSpecialOrder <order ID>
RemoveSpecialOrder <order ID>
Add or remove a special order.
If <query> ## <action if true>
If <query> ## <action if true> ## <action if false>
Check a game state query and perform an action based on the result.

For example, this only sends a mail if the player doesn't have it in their received, mailbox, or queued-for-tomorrow mail:

"ActionsOnPurchase": [
    "If !PLAYER_HAS_MAIL Current SomeFlag ## AddMail Current SomeFlag"
]
IncrementStat <stat key> [amount] Increment a stat value by the given amount (default 1) for the current player. This can be a vanilla stat key (see the PLAYER_STAT game state query for a list) or a custom stat key. The amount can be negative to decrement it.
MarkActionApplied <player> <answer ID> [applied] Mark a Data/TriggerActions entry as applied or non-applied for the specified player(s), depending on [applied] (default true). This can be used to skip or re-run an entry, since Data/TriggerActions entries are only applied once by default.

Note that an entry can't use this to mark itself unapplied; see Make Data/TriggerActions repeat if you want to do that.

MarkCookingRecipeKnown <player> <recipe ID> [known]
MarkCraftingRecipeKnown <player> <recipe key> [known]
Set whether specified player(s) know a cooking or crafting recipe, depending on [known] (default true).

Note that forgetting a recipe will also reset its times-cooked/crafted counter to zero.

MarkEventSeen <player> <event ID> [seen] Mark an event as seen or unseen for the specified player(s), depending on [seen] (default true).
MarkQuestionAnswered <player> <answer ID> [answered] Mark a dialogue answer as selected or non-selected for the specified player(s), depending on [answered] (default true).
MarkSongHeard <player> <song ID> [heard] Mark a song track's cue name heard or non-heard for the specified player(s), depending on [heard] (default true). This affects whether the song appears in the jukebox selection.
Null (Specialized) Does nothing. This is used internally; there's generally no benefit to using it yourself.
RemoveTemporaryAnimatedSprites Remove all temporary animated sprites in the current location. For example, this can be used in the event setSkipActions command to clean up the event's temporary sprites.
SetNpcInvisible <NPC name> <day duration> Hide an NPC so they disappear and can't be interacted with for the given number of days. This is used when NPCs go away for a while (e.g. Elliott's 14-heart event).

TODO: can you call this from the farmhand? Atra doesn't think so.

SetNpcVisible <NPC name> End the NPC's invisibility, if applicable.

TODO: check to see if can be called from farmhand. Will probably make the NPC visible again, but the daysUntilNotInvisible is NOT synced.

Target player

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

value result
All Apply the action to all players (regardless of whether they're online).
Current Apply to the local player.
Host Apply to the main player.

Triggers

Data/TriggerActions

Data/TriggerActions is a data asset which lets you dynamically perform actions when the conditions are met.

For example, consider this Content Patcher patch:

{
    "Format": "2.0.0",
    "Changes": [
        {
            "Action": "EditData",
            "Target": "Data/TriggerActions",
            "Entries": {
                "{{ModId}}_OnLeoMoved": {
                    "Id": "{{ModId}}_OnLeoMoved",
                    "Trigger": "DayEnding",
                    "Condition": "PLAYER_HAS_MAIL Host leoMoved",
                    "Actions": [
                        "AddMail Current {{ModId}}_Abigail_LeoMoved",
                        "AddConversationTopic {{ModId}}_LeoMoved 5"
                    ]
                }
            }
        }
    ]
}

You can read that like: "When the player is going to sleep, if Leo has moved to the valley, then send a letter and start a conversation topic".

Each entry in Data/TriggerActions only runs once by default, though you can use the MarkActionApplied action to re-enable one.

Data/TriggerActions consists of a list of models with these fields:

field effect
Id The unique string ID for this trigger action.
Trigger When to apply the trigger action. This must be one or more of these values (space-delimited):
trigger effect
DayStarted Raised when the player starts a day, after either sleeping or loading.
DayEnding Raised when the player is going to sleep. This happens immediately before the game changes the date, sets up the new day, and saves.
LocationChanged Raised when the player arrives in a location.
other Other custom triggers may be added by C# mods.
Actions (Optional) The actions to perform, as a list of strings matching the action format.
Action (Optional) A single action to perform, matching the action format.

This is just a shortcut for Actions with one action. Technically you can use both together, but usually you should just pick one property to set.

HostOnly (Optional) Whether this trigger action can only run for the main player. If true, the action will be ignored for farmhands in multiplayer.
MarkActionApplied (Optional) Whether to mark the action applied when it's applied. Default true.
  • If true: the action is added to the player's triggerActionsRun list, queries like PLAYER_HAS_RUN_TRIGGER_ACTION will return true, and the action won't run again (unless you use the MarkActionApplied action to mark it unapplied).
  • If false: the action can repeat immediately when the same trigger is raised, and queries like PLAYER_HAS_RUN_TRIGGER_ACTION will return false for it.
Condition (Optional) A game state query which indicates whether this action can be applied currently. Defaults to always true.
CustomFields (Optional) The custom fields for this entry.

Elsewhere

You can also run an action directly from...

  • A dialogue string using the $action command. For example:
    "Mon": "Hi there! Here's 10g and a parsnip, don't spend it all at once.#$action AddMoney 10#$action AddItem (O)24"
    
  • An event script using the action command. For example:
    "{{ModId}}_Event": "continue/64 15/farmer 64 16 2 Abigail 64 18 0/pause 1500/speak Abigail \"Hi. Here's 10g and a parsnip.\"/action AddMoney 10/action AddItem (O)24/pause 500/end"
    

    See also the setSkipActions command.

  • A mail letter using the %action command. For example:
    "{{ModId}}_Letter": "Hey there!^Here's 10g and a parsnip. Take care!^   -Abigail%action AddMoney 10%% %action Additem (O)24%%[#]A gift from Abigail"
    
  • The SMAPI console window using the debug action console command. For example:
    > debug action "AddMoney 10"
    
    Applied action 'AddMoney 10'.

For C# mod authors

Using trigger actions in C# data

Trigger actions are mainly meant for Content Patcher packs. C# mod can use SMAPI's events instead, which are much more flexible and efficient (unless you want to let content packs edit your trigger actions).

Extensibility

C# mods can use the StardewValley.Triggers.TriggerActionManager class to interact with trigger actions.

For example, you can...

  • Add and raise a new trigger type:
    // register custom trigger type
    TriggerActionManager.RegisterTrigger("Some.ModId_OnItemReceived");
    
    // run actions in Data/TriggerActions for the custom trigger
    TriggerActionManager.Raise("Some.ModId_OnItemReceived", new[] { item, index }); // trigger can pass optional trigger arguments
    
  • Or add a new action:
    TriggerActionManager.RegisterAction("Some.ModId_PlaySound", this.PlaySound);
    
    ...
    
    /// <inheritdoc cref="TriggerActionDelegate" />
    public static bool PlaySound(string[] args, TriggerActionContext context, out string error)
    {
        // get args
        if (!ArgUtility.TryGet(args, 1, out string soundId, out error, allowBlank: false))
            return false;
    
        // apply
        Game1.playSound(soundId);
        return true;
    }
    
  • Or run an action string:
    // NOTE: this is just an example of how to run an action. This is meant to support actions specified in data or content
    // packs. If you want to send mail (or perform other actions) in C#, it's better to call the C# APIs directly instead.
    string action = "AddMail Current Robin Now";
    if (!TriggerActionManager.TryRunAction(action, out string error, out Exception ex))
        Game1.log.Error($"Failed running action '{action}': {error}", ex);
    

To avoid conflicts, custom trigger names should be unique string IDs.

FAQs

Trigger actions vs map actions

Actions can refer to two different systems:

  • Trigger actions (this page) let you perform generic background tasks that can be done anytime, like sending a letter or starting a quest. These can be triggered automatically based on conditions, or via commands in dialogue, events, etc.
  • Map actions refer to the Action and TouchAction map properties, which do something when you walk on or interact with a map tile. These can perform a wide array of map- and interaction-specific things like showing a message box, changing the map, opening shop menus, etc. These only work in maps, and generally don't make sense in other contexts.

Aside from the similar names, they're not interchangeable and there's fairly little overlap.

Make Data/TriggerActions repeat

By default, each entry in Data/TriggerActions is only applied once per player.

There are two main ways to repeat actions:

  • To make it repeatable immediately, set "MarkActionApplied": false on the Data/TriggerActions entry.
  • To enable repeating at a different time, you can use the MarkActionApplied action to forget that it was applied. For example, this patch will set alternating 'work' or 'weekend' mail flags depending on the day of week:
    {
        "Format": "2.0.0",
        "Changes": [
            {
                "Action": "EditData",
                "Target": "Data/TriggerActions",
                "Entries": {
                    // set 'work' flag on weekdays, and reset weekend action
                    "{{ModId}}_Work": {
                        "Id": "{{ModId}}_Work",
                        "Trigger": "DayStarted",
                        "Condition": "!DAY_OF_WEEK Saturday Sunday",
                        "Actions": [
                            "AddMail Current {{ModId}}_Work Received",
                            "RemoveMail Current {{ModId}}_Weekend",
                            "MarkActionApplied Current {{ModId}}_Weekend false"
                        ]
                    },
    
                    // set 'weekend' flag on weekends, and reset work action
                    "{{ModId}}_Weekend": {
                        "Id": "{{ModId}}_Weekend",
                        "Trigger": "DayStarted",
                        "Condition": "DAY_OF_WEEK Saturday Sunday",
                        "Actions": [
                            "AddMail Current {{ModId}}_Weekend Received",
                            "RemoveMail Current {{ModId}}_Work",
                            "MarkActionApplied Current {{ModId}}_Work false"
                        ]
                    },
                }
            }
        ]
    }