Tutorial: Adding a Special Order

From Stardew Modding Wiki
Jump to navigation Jump to search

If you're interested in a breakdown of how to create a basic special order, you've come to the right place! The wiki page on Special Orders contains some basic information on what special orders are and what some of the components are, so it's recommended to read through that if you want more information on the various options.

Assumptions we're working with

This tutorial assumes you are familiar with the basics of Content Patcher (CP) formatting, the framework mod we'll use. If you're new to it, take a look at the wiki page on CP, especially the introduction to JSON.

This tutorial also assumes you know how to set up files for a content pack, specifically creating a content.json and manifest.json file (tutorial on this here).

Basics of the example

In this tutorial, we'll break down the components of a special order and show how it'd look in CP. More intermediate topics will be covered in other tutorials.

The example mod we'll work with is for Abigail to request 10 quartz and 10 amethyst, in fall and winter, and the player will have 1 week to complete the order. The player will deliver the quartz and amethyst directly to Abigail. The order will also be repeatable, and the reward will be money (4026 gold) and friendship.

NOTE: if you wanted the special order to be for a machine or other big craftable, you'll need to use a dropbox for the special order. You cannot "deliver" a big craftable to an NPC.

Name that mod and special order

First we need to devise a name for the mod and the special order, so that we can create the "order ID". This is how we'll tag the order, which will be used in Entries, as well as the i18n implementation for translations (covered in intermediate topic tutorial). In this example, ExampleMod_Abigail_CrunchyTreats is the order ID.

Note that you could have also formatted it as ExampleMod_CrunchyTreats, Abigail_CrunchyTreats, or something else entirely; it's up to personal preference.

Next, we'll need a descriptive LogName, which will make it easier to check if our order is loading using patch summary (more info on that here and here). For this example, we'll use Abigail Crunchy Treats for the LogName.

So far, this is what our patch looks like (we'll add more to it as we go on):

{
      "LogName": "Abigail Crunchy Treats", // Name here makes it easier to see the patch in the log
      "Action": "EditData",
      "Target": "Data/SpecialOrders",
      "Entries": {
        "ExampleMod_Abigail_CrunchyTreats": { }
      }
}

Content of the special order

Next we'll need the actual content of the special order: Abigail requesting 10 quartz and 10 amethyst, in fall and winter, the player to has 1 week to complete the order, and the order is repeatable. The reward will be money (4026 gold) and friendship.

Name, requester, duration, repeatability, required tags

We'll start with the name, requester, duration, repeatable, and the seasons.

{
      "LogName": "Abigail Crunchy Treats", // Name here makes it easier to see the patch in the log
      "Action": "EditData",
      "Target": "Data/SpecialOrders",
      "Entries": {
        "ExampleMod_Abigail_CrunchyTreats": {
          "Name": "Crunchy Treats", // This is the special order name that will show on the orders board and in the player journal
          "Requester": "Abigail", // Who posts the request on the orders board
          "Duration": "Week", // options are TwoDays, ThreeDays, Week, TwoWeeks, or Month
          "Repeatable": "True", // Can be true or false
          "RequiredTags": "!season_spring, !season_summer", // This order will only be available in the fall and winter.
        }
      }
}

Note that for seasons, you can either specify which season it should be available, or which season it should not be available. For our example here, I've noted in the RequiredTags section !season_spring and !season_summer separated by a comma, which is saying "not in spring and not in summer". It's a fun double negative that means the order will only be available in the fall and winter.

Other options for RequiredTags include season_X, dropbox_X, rule_X, completed_X, mail_X, event_X, knows_X, and island. Tags are likely case sensitive.

  • season_X is the generic version of the tags used in this example, for specifying season
  • dropbox_X is used if your order type is Donate, and you want to make sure the dropbox isn't already in use. X is the name of the dropbox. You'd need to add ! at the beginning, !dropbox_X, because you'd be checking it's not in use.
  • rule_X is mostly used in Qi's challenges.
  • completed_X is used to check if another special order has been used, with X representing the order ID.
  • mail_X is used to check if a piece of mail has been received by the host player, with X representing the mail ID.
  • event_X is used to check if an event has been seen by the host player, with X representing the event ID.
  • knows_X is used to check if any player (not just the host) knows an NPC, with X being the NPC name. This is case-sensitive, so make sure NPC names are capitalized!
  • island is used to check if any player has been to the island. It also applies decorative island paper on the order board.

See also: atravita's explanation of tags here.

Additional details on seasons in Required Tags

For season requirements like this, it's critical to use the RequiredTags section, which is the vanilla way to have seasonal orders, and not use CP's seasonal When check. This is because of a limitation in the way the game handles special orders.

Be very careful when adding conditions using CP's When checks. If the condition can go from false to true, for example, checking how many in-game days have passed, that's fine, as it adds a special order to the pool. The problem arises when the condition can go from true to false, such as a season check and it's no longer that season, because the special order is then removed from the pool.

It is always safer to use a tag if it's available, since it's a vanilla feature - other mods may throw a wrench into your assumptions by, for example, deleting mail flags, or resetting skill levels.

Additional sections to include

Next we will add the sections that I've taken to just leaving in there, because they seem to be part of every special order and we're not using any of them in our example (OrderType, SpecialRule, ItemToRemoveOnEnd, MailToRemoveOnEnd, and RandomizedElements).

{
        "LogName": "Abigail Crunchy Treats", // Name here makes it easier to see the patch in the log
        "Action": "EditData",
        "Target": "Data/SpecialOrders",
        "Entries": {
          "ExampleMod_Abigail_CrunchyTreats": {
            "Name": "Crunchy Treats", // This is the special order name that will show on the orders board and in the player journal
            "Requester": "Abigail", // Who posts the request on the orders board
            "Duration": "Week",
            "Repeatable": "True",
            "RequiredTags": "!season_spring, !season_summer", // This order will only be available in the fall and winter
            "OrderType": "",
            "SpecialRule": "",
            "ItemToRemoveOnEnd": null,
            "MailToRemoveOnEnd": null,
            "RandomizedElements": null,
          }
        }
}

Text and Objective Details

Next, let's add the text the player will see in the orders board and in their journal, as well as the objective information.

{
        "LogName": "Abigail Crunchy Treats", // Name here makes it easier to see the patch in the log
        "Action": "EditData",
        "Target": "Data/SpecialOrders",
        "Entries": {
          "ExampleMod_Abigail_CrunchyTreats": {
            "Name": "Crunchy Treats", // This is the special order name that will show on the orders board and in the player journal
            "Requester": "Abigail", // Who posts the request on the orders board
            "Duration": "Week",
            "Repeatable": "True",
            "RequiredTags": "!season_spring, !season_summer", // This order will only be available in the fall and winter
            "OrderType": "",
            "SpecialRule": "",
            "ItemToRemoveOnEnd": null,
            "MailToRemoveOnEnd": null,
            "RandomizedElements": null,
            "Text": "I'm craving some crunchy treats! Could someone bring me 10 quartz and 10 amethyst?", // The text shown on the special order board and the journal
          },
          "Objectives": [
                {
                  "Type": "Deliver",
                  "Text": "Deliver 10 Quartz to Abigail", // The text for the objective in the journal
                  "RequiredCount": "10",
                  "Data": {
                    "AcceptedContextTags": "item_quartz",
                    "Message": "Quartz really sparkles, doesn't it? Thanks @!$h", // What the NPC says when you deliver the item
                    "TargetName": "Abigail" // Which NPC to deliver the item to
                  }
                },
                {
                  "Type": "Deliver",
                  "Text": "Deliver 10 Amethyst to Abigail",
                  "RequiredCount": "10",
                  "Data": {
                    "AcceptedContextTags": "item_amethyst",
                    "Message": "It definitely tastes of purple!$h",
                    "TargetName": "Abigail"
                  }
                }
              ]
        }
  }

Note that the Message lines are dialogue, and the examples make use of portrait commands ($h) and a replacer command (@). You can read more about these commands on the modding Dialogue wiki page.

Rewards

The last piece of a special order are the rewards. Since this is a basic example, it's money and friendship. As you get more comfortable with modding, you may want to experiment with Conversation Topics and Events as rewards (one tips and tricks page and the Events modding wiki page). The Special Orders modding wiki page has more detailed information on rewards.

{
        "LogName": "Abigail Crunchy Treats", // Name here makes it easier to see the patch in the log
        "Action": "EditData",
        "Target": "Data/SpecialOrders",
        "Entries": {
          "ExampleMod_Abigail_CrunchyTreats": {
            "Name": "Crunchy Treats", // This is the special order name that will show on the orders board and in the player journal
            "Requester": "Abigail", // Who posts the request on the orders board
            "Duration": "Week",
            "Repeatable": "True",
            "RequiredTags": "!season_spring, !season_summer", // This order will only be available in the fall and winter
            "OrderType": "",
            "SpecialRule": "",
            "ItemToRemoveOnEnd": null,
            "MailToRemoveOnEnd": null,
            "RandomizedElements": null,
            "Text": "I'm craving some crunchy treats! Could someone bring me 10 quartz and 10 amethyst?", // The text shown on the special order board and the journal,
            "Objectives": [
                {
                  "Type": "Deliver",
                  "Text": "Deliver 10 Quartz to Abigail", // The text for the objective in the journal
                  "RequiredCount": "10",
                  "Data": {
                    "AcceptedContextTags": "item_quartz",
                    "Message": "Quartz really sparkles, doesn't it? Thanks @!$h", // What the NPC says when you deliver the item
                    "TargetName": "Abigail" // Which NPC to deliver the item to
                  }
                },
                {
                  "Type": "Deliver",
                  "Text": "Deliver 10 Amethyst to Abigail",
                  "RequiredCount": "10",
                  "Data": {
                    "AcceptedContextTags": "item_amethyst",
                    "Message": "It definitely tastes of purple!$h",
                    "TargetName": "Abigail"
                  }
                }
              ],
              "Rewards": [
                {
                  "Type": "Money",
                  "Data": {
                    "Amount": "4026"
                  }
                },
                {
                  "Type": "Friendship",
                  "Data": {} // I modeled this after how vanilla special orders are formatted
                }
              ]
          }
        }
  }

Test the special order

After writing out your order, you can run the content.json through the json validator site and set the Format to Content Patcher. This will check for syntax issues, but won't catch everything. Alternatively, you can check out how to set up the schema so that your mod is validated while you're writing it.

You can test your mod by going to the special orders board on an in-game Monday and see if it's available. Note that it may take multiple tries, as the game picks from all of the available special orders. Using the debug command debug day # to set the day to the next Sunday (based on the number, i.e. 7, 14, 21, or 28), then sleeping to Monday, and then checking the board will become part of the routine. For more information, check out the Console Commands wiki page.

If you're not seeing your special order show up (which may take multiple tries), or there are other issues, using patch summary will also become part of the troubleshooting routine.

  • Additional details on the commands available in CP like patch summary are on the troubleshooting guide under the author guide on Github.

If you made it this far, congratulations, you made your first special order mod! There are also more intermediate topics to cover, and those will be in separate tutorials.

See also