Tutorial: Converting Custom Furniture To DGA

From The Stardew Modding Wiki
Jump to navigation Jump to search

How to Convert a CF (Custom Furniture) Pack to DGA (Dynamic Game Assets)[edit | edit source | hide | hide all]

Converting a CF content pack to DGA is fairly straightforward, since most of the properties are similar/the same. There are some slight differences in how the image files are structured, and DGA has a lot more power and flexibility.

Here's a short comparison:

Pros Cons
CF
  • Furniture is sold in catalog.
  • Bugs with tables/wall furniture.
  • Bugs with game crashes.
  • Broken in 1.5.5.
  • Probably doesn't handle all types of furniture.
  • All furniture sold at Robin's with no option to change.
DGA
  • Can do seasonality inside the mod.
  • Furniture can be sold in any vanilla shop.
  • Chairs are sittable.
  • Dressers are functional.
  • Items are rotatable.
  • Does not do window change at nightfall.
  • Tables/table placement not yet fully functional.

A Note About Permissions[edit | edit source | hide]

If you're updating a mod you made, or are just doing this for your own personal game and not publishing/sharing it anywhere, you can skip this section.

If you are wanting to update a furniture mod that someone else made from CF to DGA and then publish it on Nexus, you are going to need to be very careful about checking the permissions. Most mod authors either don't permit that at all or require you to ask permission first.

Updating the Manifest[edit | edit source | hide]

You'll need to start with the basics: updating the 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. You should be able to pull most of the information from a previous pack, but make sure to add the two lines about DGA as well as update the "ContentPackFor" field.

{
  "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".

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

For DGA purposes, images may sometimes need to be rearranged so the index referencing where in your image file is capable of actually referencing each 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. You can do this in most photo editing software, but you do have to be careful that all your images end up being multiples of 16 pixels by 16 pixels, and that you don't accidentally offset your grid pattern. If you use a Mac, you can do this in Preview by opening up a copy of the original image, using rectangular select to select just the furniture you want, and cropping the image to that furniture with command-K. If you prefer a different image editor, you should be able to do a similar operation with that editor's crop tools.

Additionally, if all your furniture items are the same dimensions, life is easy and you don't have to worry—skip straight to the next step.

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

For chairs, you may need to actually do a bit of image editing yourself. This is because the chairs sometimes need to put the arm or back of the chair in front of the player, depending on the orientation of the chair. You should 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.

For more details, and some pictures to explain this more, see Tutorial: Creating Custom Furniture With DGA.

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 you have any confusion, 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
            }
        ]
    },
]

When you're converting from CF, you will be able to pull the DisplaySize directly from the width and height in the old json. The boxWidth and boxHeight should be used to calculate CollisionHeight (possibly different in different rotations). Instead of noting the number of rotations, you will just set more than one entry in the Configuration field, and you will have to manually calculate the new width/height/texture to use for each rotation.

If all of your furniture was put into different images, you can use index 0 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 name, to reduce confusion. 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. See Tutorial: Creating Custom Furniture With DGA for more details.