Changes

m
→‎Extensibility: Tweak code example (two "out string error" lines will produce a compiler error)
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 210: Line 235:  
<li>A [[Modding:Dialogue|dialogue string]] using the <samp>$action</samp> command. For example:
 
<li>A [[Modding:Dialogue|dialogue string]] using the <samp>$action</samp> command. For example:
 
<syntaxhighlight lang="js">
 
<syntaxhighlight lang="js">
"Mon": "Hi there! Here's 10g, don't spend it all once.#$action AddMoney 10"
+
"Mon": "Hi there! Here's 10g and a parsnip, don't spend it all at once.#$action AddMoney 10#$action AddItem (O)24"
 
</syntaxhighlight></li>
 
</syntaxhighlight></li>
    
<li>An [[Modding:Event data|event script]] using the <samp>action</samp> command. For example:
 
<li>An [[Modding:Event data|event script]] using the <samp>action</samp> command. For example:
 
<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.\"/action AddMoney 10/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:
 
<syntaxhighlight lang="js">
 
<syntaxhighlight lang="js">
"{{ModId}}_Letter": "Hey there!^Here's 10g. Take care!^  -Abigail%action AddMoney 10%%[#]A gift from Abigail"
+
"{{ModId}}_Letter": "Hey there!^Here's 10g and a parsnip. Take care!^  -Abigail%action AddMoney 10%% %action Additem (O)24%%[#]A gift from Abigail"
 
</syntaxhighlight></li>
 
</syntaxhighlight></li>
    
<li>The [[Modding:Console commands|SMAPI console window]] using the <samp>debug action</samp> console command. For example:
 
<li>The [[Modding:Console commands|SMAPI console window]] using the <samp>debug action</samp> console command. For example:
<pre>> debug action AddMoney 10
+
<pre>> debug action "AddMoney 10"
    
Applied action 'AddMoney 10'.</pre></li>
 
Applied action 'AddMoney 10'.</pre></li>
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]]
28

edits