Tutorial: Creating Custom Furniture With DGA

From The Stardew Modding Wiki
Jump to navigation Jump to search

How to Create Furniture with DGA (Dynamic Game Assets)[edit | edit source | hide | hide all]

This tutorial is very similar to Tutorial: Converting Custom Furniture To DGA but is specifically focused on making new furniture packs, while the other one focuses on converting furniture packs.

Creating the Manifest[edit | edit source | hide]

You'll need to start with the basics: creating a manifest.json. You will need to make sure the manifest follows DGA conventions, and that it correctly identifies your mod as a DGA content pack. Below is an example of a manifest.json for a DGA mod. Most of this should be familiar if you've ever made a content pack, but DGA has special fields for the DGA format and conditions format.

{
  "Name": "Bonsai Trees for DGA", // Your furniture mod name
  "Author": "violetlizabet", // Your name
  "Version": "1.0.1", // Version of your mod. You can either start with 1.0.0 if you're making a new Nexus mod profile, or increment the existing version.
  "Description": "Six bonsai tree decorations for Dynamic Game Assets", // A short description
  "UniqueID": "violetlizabet.DynamicGameAssets.BonsaiTrees", // Format: yourname.DynamicGameAssets.yourmodname
  "UpdateKeys": ["Nexus:####"],
  "ContentPackFor": {
    "UniqueID": "spacechase0.DynamicGameAssets", // Remember a mod can only be a content pack for one other mod
    "MinimumVersion": "1.1.0" // Current version of DGA, optional but can be useful
  },
  "DGA.FormatVersion": 2, // Current default
  "DGA.ConditionsFormatVersion": "1.23.0" // You need this to tell it which version of CP conditions to use
}

Creating content.json[edit | edit source | hide]

For DGA, every mod must have a content.json. This is the file that DGA first looks at for all of the information it needs. You can either choose to do all of your work inside this file, or you can make content.json reference any number of other json files. My personal preference is to use content.json as a reference file that points to all the other jsons, and then make one json to create all the furniture and another to sell it all.

Here's what a sample content.json that does that looks like:

[    // Loads furniture Json
    {
        "$ItemType": "ContentIndex",
        "FilePath": "Jsons/bonsai_trees.json"
    },
    // Loads shop entries Json
    {
        "$ItemType": "ContentIndex",
        "FilePath": "Jsons/bonsai_tree_shop_entries.json"
    },
]

For your purposes, you can rename "bonsai_trees.json" and "bonsai_tree_shop_entries.json" to be whatever you want, but ideally it should be something that makes sense.

Creating the Folder Structure[edit | edit source | hide]

For organizational purposes, it's easiest to create a subfolder inside your mod's folder named "Assets" to keep all of the .pngs with the textures. It's also nice to have a subfolder named "Jsons" if you have more than one json with information in it. Finally, you will need a folder named "i18n" which holds all of the display name/information as well as translations.

Here's a screenshot of what this looks like on a Mac:

FolderStructure.png

If you're interested to see it on your computer, you can download the mod referenced here. It's on Nexus and it's called "Bonsai Trees for DGA".

Creating the Source Images[edit | edit source | hide]

For DGA purposes, images need to be arranged such that the index referencing the location of a specific furniture's image in your image file is a multiple of the size of your furniture item. There is a fair bit of flexibility in how to do this, depending on how much fun you think divisibility rules are.

If you're unsure of what you want to do and you just want something simple, just put every furniture item into its own .png file, and ignore the section below. Additionally, if all your furniture items are the same dimensions, life is easy and you don't have to worry—you can also put them all in the same image file.

If you think math is fun and you'd love to do more of it, read the section below.

For chairs, since they sometimes need to put the arm or back of the chair in front of the player, depending on the orientation of the chair, you will need extra images for those front parts. You can look at the armchairs in the DGA example pack for an illustration of what this would look like.

Once you have all of your images, name them something reasonable and put them in your Assets subfolder.

(Advanced) Divisibility Rules and Why You Need Them[edit | edit source | hide]

The way that DGA references textures is through an index into the .png combined with the texture height/width. For example, if your furniture item has a texture that is 2 tiles wide and 3 tiles tall, it starts in the top left corner of the image and moves right in multiples of 2x3 tiles (32 by 48 pixel rectangles). Once it hits the right side of the image, it moves down a row by 48 pixels and resumes moving to the right. Because of this, you must make sure to put differently-sized furniture items in places that can be referenced in this way.

You can leverage divisibility to help you with doing this. For example, if you have three rows of furniture that is 2 tiles by 2 tiles, you end up back at a multiple of 3 again, and you can make the next row have furniture that is 3 tiles tall. However, the row after that is 9 tiles down, so you could either keep going with 3 tiles tall furniture, or you can put a row of 1 tile x 1 tile furniture to get you to 10 tile rows down, whereupon you can do 2 tile tall furniture again. All of this is talking about row number, but you need to do the same math in terms of columns as well, it's just that a lot of furniture is 1 tile wide and so it's easier to fit in. In general, you will have an easier time in life if you overlay a chessboard like grid of 16x16 pixels in different colors, so that you can count multiples of your furniture shape more easily.

Here's an example tilesheet, as it would look in a pixel art editor with a grid overlay set to show 16 pixel by 16 pixel squares:

Objects.png

Here's how you would count indices for any items that are 1 tile by 1 tile. Note that this sheet consists of some 1x1 objects, but also many things that are different sizes. What this creates is sort of "invalid" indices for 1x1 objects, where it would pull a texture that's a random part of other objects.

Objects 1x1.png

Here's the same thing, but for 1 tile by 2 tile objects. You can think of this the same way as 1x1 objects, but now different indices are invalid.

Objects 1x2.png

Here's the same thing, but for 2 tile by 2 tile objects:

Objects 2x2.png

Here's the same thing, but for 3 tile by 2 tile objects:

Objects 3x2.png

Here's the same thing, but for 3 tile by 3 tile objects:

Objects 3x3.png

If you compare the different grids from all the different sizes, you can see that for each item size, it has to fit nicely inside the grid of its own size, but as long as that constraint is obeyed, all the objects can be on the same sheet.

Building the Furniture Json[edit | edit source | hide]

Once your image has been rearranged/restructured if needed, you're going to need to actually make the furniture exist in-game with DGA. For each item, you're going to need one entry in the furniture.json file. You can reference the DGA documentation or existing DGA furniture packs if this example isn't making sense, but here's an example json:

[
    {
        "$ItemType": "Furniture",
        "ID": "Bonsai1",
        "Type": "Decoration",
        "Configurations": [
            {
                "Texture": "Assets/bonsai_trees.png:0", // where the image of your furniture is 
                "DisplaySize": { "X": 1, "Y": 2 }, // width and height of your furniture image
                "CollisionHeight": 1, // how tall the base (non-walk-through area for most furniture) of your furniture is
            }
        ]
    },
]

Some furniture types are a little more detailed: for example, chairs need an additional reference to the front image added as a FrontTexture. You should reference the section "Special Types" under the Furniture section of the documentation here: [[1]]

If all of your furniture was put into different images, you can use index 0 in the texture field for all of them. If they're all in the same image because they're the same size, you just count moving left to right through a row and then down, starting in the top left corner, and remember it's zero-indexed. If they're different sizes in the same image, I hope you read the section above and understand the indexing system well enough to calculate what index you want.

For the ID, you probably want to pick something that is not the same as the display name (name you see in game), to reduce confusion between the two, but still is something that references the item specifically. Ideally it's also something that makes sense and is reasonably organized.

This is a really basic furniture item that only has one rotation to it, and is the most basic type (Decoration). For more complicated types of furniture, you're probably going to want to reference some existing content packs or the DGA example pack.

Adding Names and Descriptions in the Internationalization Folder[edit | edit source | hide]

DGA uses the i18n format for referencing names/descriptions of items. You will need to make a default.json that contains all of the names/descriptions of your furniture items in English, and then optionally you can make jsons for other languages as well. You can pull the names/descriptions directly from the CF json.

In order to reference an item of furniture, you'll need to make sure to replace Bonsai1 below with the ID you set in your furniture creating json file. You need to set both a name and a description in order for it not to show up as a really long string of nonsense in-game.

Here's an example of what default.json would look like:

{
  "furniture.Bonsai1.name": "Bonsai #1",
  "furniture.Bonsai1.description": "A miniature wisteria tree in an orange pot.",
}

If you want to make translation files, you can make other jsons using the format described here: [SDV Wiki Page on Translations]

Here's an example of what the corresponding es.json would look like:

{
  "furniture.Bonsai1.name": "Bonsái #1",
  "furniture.Bonsai1.description": "Una glicina en miniatura en una maceta naranja.",
}

Building the Shop Entries Json[edit | edit source | hide]

Once you've created all of the furniture in DGA, you're going to need to make a way to sell it. Right now, DGA does not have any way to add the new furniture created to the Furniture Catalog, and CJB Cheats does not handle DGA objects. In theory, users could use debug commands to spawn in all their new furniture, but this is not very user-friendly and breaks immersion.

Each furniture item is going to need at least one shop entry. A shop entry is basically a DGA json entry category that causes one thing to be sold at one shop. If you want to sell your item at more than one shop, you can make more than one shop entry.

Here's an example of what a shop entry would look like:

[
  // Sell bonsai at Robin's
  {
    "$ItemType": "ShopEntry",
    "Item": { "Value": "violetlizabet.DynamicGameAssets.BonsaiTrees/Bonsai1" },
    "ShopId": "Carpenter",
    "MaxSold": 1,
    "Cost": 1000,
    "DynamicFields": [
      {
        "Conditions": { "DayOfWeek |contains=Monday": false},
        "Enabled": false,
      }
    ]
  },
]

Note that the Item field must use the full ID including your mod name to work. The list of possible vanilla Shop IDs is here: [DGA Documentation Page]

If you want to sell unlimited numbers, remove the MaxSold line altogether. The Cost you can pull directly from your old json.

Dynamic fields are a powerful new tool in DGA that allows you to set CP-style conditions for when to do things. In this case, what this does is set the furniture to be sold only on Mondays. You can also use EnableConditions in this case to accomplish the same goal, which would look like this:

[
  // Sell bonsai at Robin's
  {
    "$ItemType": "ShopEntry",
    "Item": { "Value": "violetlizabet.DynamicGameAssets.BonsaiTrees/Bonsai1" },
    "ShopId": "Carpenter",
    "MaxSold": 1,
    "Cost": 1000,
    "EnableConditions": {
        "DayOfWeek": "Monday",
    }
  },
]

More Advanced Stuff[edit | edit source | hide]

Furniture in DGA is actually extremely powerful due to addition of Dynamic Fields and the option for Tile Properties to be added to furniture. It may take a little practice to be able to use this power.

Seasonality[edit | edit source | hide]

In order to have seasonal furniture, you can leverage Dynamic Fields to change the texture depending on the season. Here's an example (from "Lumisteria DGA Furnitures" on Nexus, the Lumisteria DGA Flower And Plant Set file):

[
    {
        "$ItemType": "Furniture",
        "ID": "SeasonalPlant18",
        "Type": "Decoration",
        "Configurations": [
            {
                "Texture": "Assets/lumisteria_flowerset.png:12",
                "DisplaySize": { "X": 1, "Y": 2 },
                "CollisionHeight": 1,
            }
        ],
        "DynamicFields" : [
            {
                "Conditions": { "Season": "spring"},
                "Configurations[ 0 ].Texture": "Assets/lumisteria_flowerset.png:8",
            },
            {
                "Conditions": { "Season": "summer"},
                "Configurations[ 0 ].Texture": "Assets/lumisteria_flowerset.png:9",
            },
            {
                "Conditions": { "Season": "fall"},
                "Configurations[ 0 ].Texture": "Assets/lumisteria_flowerset.png:10",
            },
            {
                "Conditions": { "Season": "winter"},
                "Configurations[ 0 ].Texture": "Assets/lumisteria_flowerset.png:11",
            }
        ],
    },
]

Note that what you are doing in this case is going one level deeper, and referencing into one of the entries of Configurations. If you want to change the texture of the other rotations of your furniture, then you can change the 0 to be the index of whichever rotation you want.

Tile Properties[edit | edit source | hide]

The DGA example pack has an example of assigning TileProperties to furniture. This a powerful but somewhat unexplored new feature. These is an example of a very basic action in "Lumisteria DGA Furnitures" on Nexus, the Lumisteria DGA Flower And Plant Set file. For the water-like decorations, it has the water tile property, making it possible to fill watering cans there.

For another example of assigning TileProperties, you can look at "Magic Furniture" on Nexus. This mod makes use of the "Back TouchAction MagicWarp" tile property, the "Buildings Action Warp" tile property, and the "Buildings Action EnterSewer" tile property. For a list of most of the tile properties in game, see the Stardew Valley wiki modding page on Maps.