Skip to content

Instantly share code, notes, and snippets.

@manavortex
Last active February 8, 2024 16:12
Show Gist options
  • Save manavortex/dd991af1704ac97800887705c17bcdcc to your computer and use it in GitHub Desktop.
Save manavortex/dd991af1704ac97800887705c17bcdcc to your computer and use it in GitHub Desktop.
WIP of guide how to add items via archiveXl

The final version of this guide can be found here!

This guide was created after reading this one and being left with a bunch of question marks. The good people on the modding discord have been extremely helpful and clarified many questions. To get a deeper understanding, refer to the initial guide and follow the linked resources.

Assumed skill level: You should be able to find your way around WolvenKit, but I aim to keep this as noob-friendly as possible.

You will find the framework's official documentation here.

Parts of this guide

  1. Getting the right files and setting up the folder structure
  2. Explanation of each file for the purpose of this guide
    ℹThis section contains a diagram with how the files interact!
  3. Putting the right content into the files, hooking them up the way it's shown in the diagram
  4. Adding appearances

Adding items to Cyberpunk

Perquisites

Perquisite
Necessary mods TweakXl, ArchiveXl, and Red4ext.
WolvenKit WolvenKit Nightly

⚠This tutorial has been created with the bleeding-edge WolvenKit Nightly. I recommend that you grab at least 2022-11-02, unless yours is more recent — it has no breaking bugs, and all files will get copied reliably.
If you're using an older version of WolvenKit, the folder structure for ArchiveXL items will not match the one assumed below, and maybe not all files get copied to "packed".


Get the files, create the structure

👉 TL;DR: If you can't be arsed doing this by yourself, find a template project with one working item (female rigged) here.

This part of the guide deals with setting up the files and putting them in the right place. After that, there'll be an explanation of what they do, and finally, a step-by-step guide of filling them with content.

We'll assume that your mod is named my_shirt and will live in the folder archive\tutorial:

tutorial\my_shirt

Create a subfolder with the name of ops (for 'operations'). Here we'll put those files that organize how the items correspond to the appearances.

You should end up with the following file structure:

archive  
  - base  
  - tutorial  
        - myshirt  
        - ops  

ℹIt is good practice to have a folder for your modded stuff directly under archive. That way, base is reserved for the non-modded base game items.

As for an explanation which file does what, see the next section "What do these files do".

Grab entity and app files

Now, add the following files to your project and move them to tutorial\my_shirt:

  1. base\gameplay\items\equipment\underwear\player_underwear_item.ent
  2. base\gameplay\items\equipment\underwear\appearances\player_underwear_item_appearances.app
  3. base\characters\garment\player_equipment\torso\t1_080_pwa_tank__judy.ent
    (any file from this folder will do)
  4. your mesh (I'll use base\characters\garment\gang_monk\torso\t2_135_jacket__monk_shirt\t2_135_wa_jacket__monk_shirt.mesh)

and the operative files (these go into the subfolder tutorial\ops):

  1. base\gameplay\factories\items\clothing.csv
  2. base\localization\en-us\onscreens\onscreens.json

Rename them as follows:

file name new file name
player_underwear_item.ent rootentity.ent
player_underwear_appearances.app appearance.app
t1_080_pwa_tank__judy.ent mesh_entity.ent
t2_135_wa_jacket__monk_shirt.mesh my_mesh.mesh
file name new file name
clothing.csv my_cool_new_items.csv
onscreens.json translation_strings.json

⚠Do not edit those files outside of WolvenKit!
If you want to do that, you need to export them to json first (yes, even the json).

❗❗❗ Whatever you do, do not let Microsoft Excel touch the clothing.csv.

It is good practice to keep local copies of everything that you change (=> custompathing) instead of overwriting files under base. This makes sure that no other mods will overwrite your changes.
ℹ Only keep files under base if you are okay with them being overwritten!

Add the .archive.xl and tweaks.yml files

Create a new file by using WolvenKit's toolbar button.

  1. Select "ArchiveXL" on the left. This file will later go into gamedir\archive\mods.
    Call it yourModName.archive.xl, so that it'll be directly below yourmodname.archive.
  2. Select "ArchiveXL" on the left. This file will later go into r6\tweaks.
    Call it yourModName.yaml.

⚠As of today (October 30 2022), WolvenKit can't edit the archive.xl file. You'll have to use a text editor such as Notepad++.

You should now have the following file structure:

tutorial  
    myshirt  
      - appearance.app  
      - mesh_entity.ent  
      - my_mesh.mesh  
      - rootentity.ent  
 ops  
   - my_cool_new_items.csv  
   - translation_strings.json  
resources                    << from WolvenKit-8.7.1-nightly.2022-11-01  
  - yourModName.archive.xl  
  - yourModName.yaml         

ℹ Before WolvenKit Nightly 2022-11-01, yourModName.yaml will be located in the folder ArchiveXL. I recommend updating.

👉 If you plan on adding more than one clothing item, I recommend keeping the translation_strings.json in the clothing item's subfolder — that way, you can have multiple files for multiple items, rather than have everything in one.

Optional, but very recommended: Clean out obsolete entries

  1. Open the file translation_strings.json in WolvenKit.
    Expand the array root and then the array entries. Delete all entries but one.

  2. Open the file my_cool_new_items.csv in WolvenKit.
    In compiledData, delete all entries but one. In data, delete everything - these will get autogenerated.

  3. Open the file rootentity.ent.

    Expand the list appearances. Delete all entries but the first (most likely default).

  4. Open the file appearance.app.
    Expand the list appearances. Delete all entries but default.

  5. Open the file mesh_entity.ent
    Select resolvedDependencies and delete all the entries. (We don't need Judy's top anymore.)

  6. Make a back-up copy of your mesh, then open it.

    1. Expand the list materials. Open the appearance default and check which material is linked in the chunkMaterials array.

    2. Expand localMaterialBuffer.materials . Delete all entries that aren't used in default.
      ℹ Some meshes don't have localMaterialBuffer — look for preloadLocalMaterialInstances instead

    3. Expand materialEntries. Delete all entries that you also removed from localMaterialBuffer.
      👉 You should be left with 1-3 materials.

    4. Now we need to adjust the indices.
      If you make any mistakes here, the wrong material will be loaded.

      Select the first CMeshMaterialEntry in materialEntries and check the property index — it needs to be 0.

    5. Browse through each entry that is still in the list and make sure that the index is sequential.
      ℹ You can see in the list which index each item needs to have:
      index explanation

✅ Now is a good time for a backup.

Explanation: What do these files do?

Here is a diagram of how the files connect to each other.

ℹ If you are renaming or moving files, you might want to refer to this diagram. It's what I do.

  • As you can see, there are two entry points, the yaml and the archive.xl.
  • The yaml and the csv together load the rootentity, which is responsible for displaying the correct mesh.

my_cool_new_items.csv: the 'factory'.

ℹ You need to touch it only once, unless you want to add new files.

The factory connects yourModName.yml via entityName to the corresponding rootEntity.ent.

When reading yourModName.yml (explained right after this one), the game will find entries like this:

Items.my_shirt:  
  $base: Items.GenericFootClothing  
  entityName: my_shirt                       << you set this field  
  appearanceName: my_shirt_  
  displayName: my_shirt_name  

When adding the new items, the entries in the factory will be filtered by entityName for a match in the first field:

{  
	0: my_shirt                              << as corresponding to entityName in appearance.app  
	1: tutorial\myshirt\rootentity.ent        << this file will be used to resolve the appearances  
},   
{  
	0: my_boots                               << this does not match  
	1: tutorial\myshirt\rootentity.ent        << so this file won't matter here  
}  

… and use rootentity.ent to look up an appearance by the name of my_shirt_.

yourModName.yaml

This file controls the adding of items to the game. You will touch it every time you add another colour variant or additional items.

ℹ You need to touch this file every time you add a colour variant or a new item.

An entry looks like this:

Items.my_shirt:                          << name of the item in game (for spawn code)  
  $base: Items.GenericInnerChestClothing  << select the type of clothing you want  
  entityName: my_shirt                   << maps to my_cool_new_items.csv by entityName => data[x][0]  
  appearanceName: my_shirt_              << points to rootentity.ent. The trailing _ is on purpose!  
  displayName: my_shirt_name             << points to translation_strings.json  
  localizedDescription: my_shirt_desc    << points to translation_strings.json  
  quality: Quality.Legendary              << we don't want the cheap garbage, do we now  
  appearanceSuffixes: []                  << See the section "suffixes" below for an explanation  

This will let you add the item via Game.AddToInventory('Items.my_shirt').

Three mappings take place here:

  1. entityName: Basically, the game will look through my_cool_new_items.csv until it finds an array where the value for [0] (the first entry) is identical to this key. It will then use the file specified in [1] (the second entry) to look up the appearanceName.
  2. appearanceName: In the entity specified via csv, it will look for an appearance by this name.
    ℹ The name will only be considered up to the first suffix - that is, everything before the & will be ignored.
  3. displayName/localizedDescription : In the translation_strings.json, find an array where the value for [3] (the last entry) is identical to this key. Then, check which gender V has, and display either femaleVariant or maleVariant.

$base defines which type of clothing to use. All those types have different properties (e.g. on which slot they are), and they inherit properties from their parent template, which can cause problems (see "Suffixes, and whether you need them" below for more detail).

The following types of clothing exist:

  • Items.GenericHeadClothing
  • Items.GenericFaceClothing
  • Items.GenericOuterChestClothing
  • Items.GenericInnerChestClothing
  • Items.GenericLegClothing
  • Items.GenericFootClothing

⚠When editing this file, please keep in mind that indent is important! The first line of each block must not have spaces, the following lines must have the same amount of spaces.

yourModName.archive.xl

This file tells the game to load the factory (my_cool_new_items.csv) and the localization file (translation_strings.json).

ℹ You need to touch it only once, unless you want to add new files.

As for the content, see the section "Setting up the files" below.

translation_strings.json

This is the localization file which tells the game which texts to display.

ℹ You need to touch this file every time you add a new text string.

An entry will look like this:

femaleVariant: My shirt - Babydoll  
maleVariant: My shirt - Sleeveless  
primaryKey: 0  
secondaryKey: my_shirt_name  

The value under secondaryKey must match the entry in yourModName.yml, or you'll just see an empty string.

⚠If you don't need a male-specific translation, you can leave it blank — by default, femaleVariant will be used.

rootentity.ent

This file defines which components should be loaded - that's why you should copy one instead of creating from scratch.

ℹ You need to touch this file every time you add a colour variant.

For our purposes, this is a glorified lookup map, translating between the key appearanceName in yourModName.yaml, a specified app file, and an appearance as specified in that file:

yourModName.yaml

   $base: Items.GenericInnerChestClothing  
   entityName: my_shirt  
   appearanceName: my_shirt_  

knows to look in my_cool_new_items.csv for an entry matching the entity name, then open the corresponding app file specified there, and find the appearance by the name of appearanceName.

An entry will look like this:

appearanceName:        my_shirt  
appearanceResource:    DepotPath: tutorial\my_shirt\appearance.app  
                       Flags:     Default  
name:                  my_shirt_&Suffixes  

For an explanation of the '&suffixes' part, see the section below.

ℹIf you do not need Suffixes (which includes not knowing what you would want them for), you should leave them out.
In that case, make very sure that your .yaml contains appearanceSuffixes: [] in each entry.

mesh_entity.ent

For our purposes, this is just a collection of components.

ℹOnce you've set this up, you won't have to touch this file again - it's on a once-per-mesh basis.

An entry looks like this:

components: [  
	0: Component {…}                                      << ignore this  
	1: entGarmentSkinnedMeshComponent {                   << the component loading our custom mesh  
		  mesh:  
		    DepotPath: tutorial\my_shirt\my_mesh.mesh    << path to your mesh  
		    Flags:     Default                            << leave this alone  
		  name: my_shirt                                 << see below  
	}  
]  

The name will be used in appearance.app's materialOverride array (see below)

Why this file?

You don't strictly need it, as you could define the components array in the individual appearances in the appearance.app. However, in the interest of not repeating ourselves, we bundle everything here. For example, if you re-name your mesh, you'll have to touch one file, rather than 25 different appearances in an array!

appearance.app

Contains a list of appearances as mapped by rootentity.ent. It will indicate which mesh to display by loading mesh_entity.ent , and overriding appearances.

ℹYou will touch this file every time you add a colour variant.

An entry will look as follows:

appearances:  
  handle:appearanceAppearanceDefinition: [  
    {  
        components:	    []                   << replaced by mesh_entity.ent  
        name:  			my_shirt           << this maps to the field 'appearanceName' in rootentity.ent  
        partsOverrides: appearanceAppearancePartOverrides[]: [{  
          partResource: tutorial\my_shirt\mesh_entity.ent  
          componentOverrides: [{  
          	componentName: my_shirt        << corresponds to the component's name in mesh_entity.ent, see above  
          	mesh_appearance: colourset_01   << corresponds to the component's appearance in my_mesh.mesh  
          }]  
        }]  
        partsValues: appearanceAppearancePart[] = [{	  
          resource: tutorial\my_shirt\mesh_entity.ent  << which entity file to load?  
        }]  
    }  
  ]  

partsValues will define what entity files to load (as a list of components), while partsOverrides tells the mesh which appearance to use.

Mesh

You probably know this, but since I'm already writing a guide, I figured I might as well be thorough.

The mesh file is the actual game object, which is made out of vertices, edges and faces.

It also holds the information on how the mesh deforms with the rig - without this, V's rig would move and the mesh would stay in place. This information is in the weights — different vertices are assigned to vertex groups which correspond to the name of the rig's bone (such as RightUpLeg or LeftLegUp) , and will then move when the bone does.
👉How much a vertex moves will be determined via vertex weights, which you can change in Blender's weight paint mode. Everyone I've ever talked to hates these things, so if you happen to be good at them and enjoy the process, please get in touch, senpai. ~manavortex

Suffixes, and whether you need them

Suffixes tell the game which appearance to load under certain circumstances. Which ones will be considered depends on appearanceSuffixes: [ … ] in the .yaml file.

⚠Your item might inherit the suffix setup from the component you specify it as. In the example of Items.GenericHeadClothing, that will be

appearanceSuffixes: [ itemsFactoryAppearanceSuffix.Gender, itemsFactoryAppearanceSuffix.Camera ]  

A mesh will first look for an appearance without any suffixes at all, and then always try to load the most specific appearance. In the example above, if you give the name of my_shirt_, it will append the suffixes and (for a female V in third person camera mode) try to find my_shirt_&Female&TPP.
my_shirt_, will be loaded.
my_shirt_&Female will be ignored.

This can be the reason why your item is invisible!! You can and should disable the suffixes if you don't need them.
To do so, add an empty array to the yaml entry:

appearanceSuffixes: []  

For clothing items, the following suffixes are relevant:

Suffix Explanation
itemsFactoryAppearanceSuffix.Gender This item is gendered
When resolving the appearance name via rootentity.ent, the game will look for appearanceName&Female and appearanceName&Male.
itemsFactoryAppearanceSuffix.Camera This item has special rules for first and third person camera
When resolving the appearance name via rootentity.ent, the game will look for appearanceName&FPP and appearanceName&TPP.
itemsFactoryAppearanceSuffix.Partial If the current item has hide_T1part part and slot OuterChest is not hidden, will search rootentity.ent for&Full or &Part
itemsFactoryAppearanceSuffix.HairType Defines how your item will look if a certain hair type is loaded (e.g., hide the back half of a bandana for long hair).
When resolving the appearance name via rootentity.ent, the game will look for &Short, &Long, &Dreads, &Buzz, &Bald

For a more in-depth list, see here.

Setting up the files

ℹ Unless otherwise specified, it'll be assumed that you edit these files in WolvenKit.

Edit the .xl file (text editor)

Put the following content:

factories:  
  - tutorial\ops\my_cool_new_items.csv  
localization:  
  onscreens:  
    en-us: tutorial\ops\translation_strings.json  

Save and close the file. You do not need to touch this again unless you want to add more files or change paths.

Edit translation_strings.json (text editor)

Find the array root, then find the array entries. (After the optional step in the setup, it should contain only one entry.)
Change it as follows:

localizationPersistenceOnScreenEntry - []   
	femaleVariant: My shirt (unisex)  
	maleVariant:  
	secondaryKey:  my_shirt_name  

Duplicate the item by right-clicking on it. Change the new item as follows:

localizationPersistenceOnScreenEntry - []   
	femaleVariant: A generic T-shirt. Can be worn by pretty much everyone.  
	maleVariant:  
	secondaryKey:  my_shirt_desc  

ℹ The second part is what you do every time you want to add a new text, such as names for colour variants or different descriptions.

Edit the factory (my_cool_new_items.csv)

  1. Find the list compiledData. It should have only one entry after you cleaned up the files.

  2. Select the item and duplicate it, then select your new entry. (arrayString)

  3. It should have three (3) items. Set the values as follows:

    0: my_shirt  
    1: tutorial\myshirt\rootentity.ent  
    2: true  
    

ℹ The list data will get overwritten with the values from compiledData.

Edit the mesh_entity.ent

Find the first component of the type entGarmentSkinnedMeshComponent. Set the following values:

mesh:   DepotPath:   tutorial\my_shirt\my_mesh.mesh      << path to your mesh  
        Flags:       Default                              << leave this alone  
name:   my_shirt    << this corresponds to the appearanceOverride in appearance.app  

Remember the value for name, as you'll use it in your rootentity.ent a lot.

Add an item to yourModName.yaml

⚠Since November 1st, 2022, WolvenKit can edit yaml files. With older versions, please use a text editor.

Create the first entry in yourModName.yaml, so that you can spawn it via Game.AddToInventory('Items.my_shirt', 1). You can copy and paste the part below.

The total file content should be:

Items.my_shirt:  
  $base: Items.GenericInnerChestClothing  
  entityName: my_shirt					  
  appearanceName: my_shirt_		  
  displayName: my_shirt_name			  
  localizedDescription: my_shirt_desc  
  appearanceSuffixes: []  

In wKit:

Edit the rootentity.ent

This is a lookup table between the key appearanceName in the yaml. It specifies which appearance (appearanceName) to look up in which file (appearanceResource/DepotPath).

  1. Find the array "appearances" - nothing else here interests us at all. There should be only one entry left.

  2. Select it and put the following details:

    appearanceName:        my_shirt         << as corresponding to the appearance in the mesh itself  
    appearanceResource:    DepotPath: tutorial\my_shirt\appearance.app  << path to your app file  
                           Flags:     Default  
    name:                  my_shirt_        << as corresponding to appearanceName in the *.yaml  
    

This connects your entry Items.my_shirt from yourModName.yaml via name with the file specified in appearanceResource, where it will then load the appearance entry corresponding to appearanceName. (Which we are about to create.)

Edit the appearance.app

  1. Find the array appearances - nothing else here interests us at all.

  2. Delete all entries but default

  3. Expand the entry default

  4. Find the array partsValues.

    1. Delete all entries.

    2. Create a new entry. Set the following value:
      resource: tutorial\my_shirt\mesh_entity.ent

      ℹ This file specifies which components to load.

  5. Find the array partsOverrides.

    1. Delete all entries.

    2. Create a new entry. Set the following value:
      DepotPath: tutorial\my_shirt\mesh_entity.ent

    3. Select the array componentsOverride and create a new item. Set the following values:

      componentName: my_shirt (as specified in the mesh_entity.ent)

      meshAppearance: default

      ℹ This will make sure that the component my_shirt will be displayed with its appearance default, as specified in the mesh.

✅ Regardless of whether or not this actually works, this is a great moment for a back-up!

Testing

Make sure all files are present

In WolvenKit, click the "Install" button. There will now be a folder "packed" in your mod directory, right next to the folder "source".

Check if it includes the following file: <yourModDir>\packed\r6\tweaks\yourModName.yaml. If it is missing, you need to copy the <yourModDir>\source\tweaks directory over.

Start the game

Press "Install and Launch" in WolvenKit. This will do the following things:

  1. Copy all supported file entries from source to their destination under packed, overwriting older files
  2. Pack an archive in packed\mod\archive, overwriting the last version
  3. Copy all files under packed into your game directory, again overwriting older files
  4. Launch the game.

You can now spawn and equip your item by running the following command (as specified in your yaml):

Game.AddToInventory("Items.my_shirt")  

You should now see your item. If not, consult the section Troubleshooting below, or retrace your steps and make sure that everything works before proceeding to the step below.

Adding an appearance (example: blackred)

⚠Before you add an appearance, make sure that your item is loading up correctly. If you have to debug, you will have to look through every appearance you made!

To add one, you will have to touch the following files:

  1. *.yaml: Adding an entry

  2. appearance.app: Adding a mapping between rootentity and mesh's appearance

  3. rootentity.ent: Adding a mapping between yaml's appearance and app's appearance

  4. *.mesh:

    1. Adding a MeshMaterialEntry
    2. Adding a MaterialInstance
    3. Adding a material
    4. Connecting those things

Step 1: Register it in your *.yaml

  1. Duplicate the entire appearance block for an already working item.
    ⚠Mind the indent!

  2. Change the first line to a unique name like Items.my_shirt_blackred

  3. Set the new appearance name for the rootentity.ent
    appearanceName: my_shirt_blackred_

  4. For lookups in your translation file (translation_strings.json): Change the values of displayName and localizedDescription to the corresponding secondary keys in the json file.
    This is optional.

      displayName: my_shirt_blackred_name			  
      localizedDescription: my_shirt_blackred_description  
    

    ℹIf you make any mistakes here, the worst that happens is an empty string.

    Example:
    yaml: displayName: my_shirt_nameblackred_name
    json:

    localizationPersistenceOnScreenEntry - []   
    	femaleVariant: my item - now in black and red  
    	maleVariant:  
    	secondaryKey:  my_shirt_name_blackred_name  
    

The total entry should look like this:

Items.my_shirt_blackred:  
  $base: Items.GenericInnerChestClothing  
  entityName: my_shirt					  
  appearanceName: my_shirt_blackred_		  
  displayName: my_shirt_blackred_name			  
  localizedDescription: my_shirt_blackred_description  
  appearanceSuffixes: []  

Step 2: Add it to the rootentity.ent

Expand the list appearances and duplicate your already working entry.

Change the following fields:
appearanceName => everything before the & must match appearanceName in your *.yaml
name => must match the name you're going to put in your app.app

Example:

old:

	appearanceName: my_shirt  
	name: my_shirt_&TPP  

new:

	appearanceName: my_shirt_blackred  
	name: my_shirt_blackred_&TPP  

ℹ You do not need to change the appearanceResource.

Step 3: Add it to appearance.app

Duplicate your already working entry and name it the way you've just defined as appearanceName in the rootentity.ent.

ℹ If you're missing any of the items below, go back to "Edit the appearance.app" above. If everything is beyond broken, restore the file from one of your backups.

  1. Open the .app file and expand the list appearances.
  2. Duplicate an item and select the new one.
  3. In the new appearance, find the array partsOverrides and expand it.
  4. Select the item inside.
  5. Find and expand the Array appearanceAppearancePartOverrides and expand it.
  6. Select the first item.
  7. Open the array componentsOverride and select the first item.
  8. Change the value of meshAppearance to the name of your new appearance in the mesh:
componentName: my_shirt             << no need to change this  
mesh_appearance: black_red           << corresponds to meshMeshAppearance.name in my_mesh.mesh  

ℹ You can leave partsValues alone - this just points at the file that loads the mesh, and you've already set it up above when setting up the file.

Step 4: Add it to the .mesh

This tutorial assumes you already know how to recolour an item. Quick reminder about the mlsetup:

  1. Export it to json
  2. put the json file in the same directory as the original .mlsetup
  3. edit the mlsetup.json with the MLSetupBuilder (there's a WolvenKit plugin now)
  4. Save it under the new name.

Now we're adding the new appearances:

  1. Find the array materials and open it.

    1. Duplicate the last entry. (Yes, use the last one, please, it's important for step 3).

    2. Select the new last entry

    3. Set the following values:

      name:  mat_black_red             <<< will be used by meshMeshAppearance in appearances[]  
      index: <index of item in array>  
      

      If you duplicated the last material, you can just increase it by one.

  2. Find the array localMaterialBuffer and open it

    1. Duplicate any entry with an mlsetup (You will see an entry MultilayerSetup under values)
    2. Drag it to the last position of the array (that's important, because the materials entries match by numeric index)
    3. Select the new item, open values, and select MultilayerSetup.
    4. Set baseMaterial/DepotPath to the .mlsetup file that you want.,
  3. Find the array appearances and open it.

    1. Duplicate any material.

    2. Change the name to the one you've defined in the appearance.app above (in this case: black_red):

      name: black_red  
      
    3. Select the array chunkMaterials and change the entries in the right-hand panel to use the material you defined in step 1.

Test it

Now, log into the game and spawn the item variant. The name is in the yaml file, in this case

Game.AddToInventory('Items.my_shirt_blackred')  

Preview images

Okay, now you've added something! But it doesn't have a preview icon yet - you'll have to add this manually.
For this purpose, you'll have to edit an image — for this, I recommend paint.net, (free)

⚠You can not use Photoshop for the final step. Or rather, you can, but the transparency will be botched. ⚠

Cyberpunk uses xbm as format for its textures. These textures are then mapped (divided into slices) by inkatlas files. The individual slots can then be used by the game for pretty much everything from UI elements to phone call icons — and image previews.

Setting up the files

First of all, download the template archive (kindly provided by Apart). This includes the following files:

Template Size of slot image
5_outfits 160x320 (x5)
5_weapons 360x120 (x5)
40_items_inkatlas_template 160x160 (x40)

As for how to take image previews, refer to this guide, section "Making the icon". This guide assumes that you have a bunch of ready-made icons lying around.

Select 40_items_inkatlas_template and copy the .inkatlas and the .xbm tutorial\ops. I'll rename them to preview_icons , future me will be grateful:

tutorial  
	ops		   
      	- my_cool_new_items.csv  
      	- translation_strings.json  
      	- preview_icons.inkatlas       <<<  
      	- preview_icons.xbm            <<<  

Getting the preview in place

Open 40_item_template.pdn in paint.net and put all your icons in. Hide the background layer once you're done, and save it as png under

<yourModDirectory>\raw\tutorial\ops\preview_icons.xbm  

ℹYou can also export preview_icons.xbm via WolvenKit, and the resulting preview_icons.png in paint.net!

Once you're done, import it back. Use the following settings:

You can now open preview_images.xbm in WKit, it should have your items.

Hooking up the inkatlas

  1. Open the file in WolvenKit.
  2. Open the list slots.
  3. For the first two inkTextureSlot items, set the value for DepotPath to the relative path of your xbm (tutorial\ops\preview_icons.xbm):
  4. Save the file. Currently (October 20022), WKit doesn't yet auto-update, so you need to close and re-open your inkatlas.
  5. You now have a tab "PartMapping". You can now see which texture corresponds to which slot:

Adding it to the yaml

We add three more lines to the item:

Items.my_shirt_blackred:  
  $base: Items.GenericInnerChestClothing  
  entityName: my_shirt					  
  appearanceName: my_shirt_blackred_		  
  displayName: my_shirt_blackred_name			  
  localizedDescription: my_shirt_blackred_description  
  appearanceSuffixes: []  
  icon:  
    atlasResourcePath: tutorial\ops\preview_icons.inkatlas  
    atlasPartName: slot_16  

That's it! Time to test!

⚠ Again, please note that indent is crucial here, as it determines the node structure. The first line needs to have an indent of 0, the lines from $base to icon need to have two spaces, and the lines atlasResourcePath and atlasPartName need to have four.

Atelier integration

This section will detail how to add an Atelier store with your items.

⚠You need to install the VirtualAtelier mod for this.

Generating an icon

First of all, download the template archive (kindly provided by Apart) and find the folder virtual_atelier_inkatlas_icon_template.

atelier_icon_template.inkatlas                        << map for the game  
atelier_icon_template.png                             << 200x200px image for a final slot  
atelier_icon_template.xbm                             << game texture  
virtual_atelier_png_to_xbm_import_settings.png        << image, also embedded here  

Put the .inkatlas and .xbm into your ops folder under tutorial\ops and rename them to atelier_icon:

tutorial  
	ops		   
      	- my_cool_new_items.csv  
      	- translation_strings.json  
      	- preview_icons.inkatlas       
      	- preview_icons.xbm            
      	- atelier_icon.inkatlas        <<<  
      	- atelier_icon.xbm             <<<  

Now, create your icon. The process is the same as in the section "Preview images". The resulting slot will be named slot_01.

Creating the atelier file

  1. Visit this website and fill out the information:
Field Content
Store ID (characters only, w/o spaces, '-', '_' and etc.) MyTutorialAtelierStore
Store Name My Tutorial Atelier Store
Atlas Resource tutorial/ops/atelier_icon.inkatlas
⚠Make sure to replace all backward slashes with forward slashes here!
Texture Part slot_01
  1. Now, add your item.
Field Content
TweakDBID Items.my_shirt
Price Whatever price you want, 0 means that it's free
Quality Are we settling for anything but legendary here?

​ Ignore "Icon path" and "description", we have these in the item itself.

  1. Click "Add Item". Repeat the process with as many items as you want.
  2. Click "Generate".

Placing the atelier file

Move the atelier file from your download folder to WolvenKit's resources folder:

resources
  - MyTutorialAtelierStore-atelier-store.reds         << new file
  - yourModName.archive.xl  
  - yourModName.yaml         

Now, it's time to test! Install the mod, then start Cyberpunk via start menu shortcut.

⚠As of today (3rd of November 2022), starting Cyberpunk via WolvenKit will not generate your atelier store. You need to first install the mod, then start the game via start menu or GOG launcher.

Troubleshooting

My item doesn't have a name / description!

Something went wrong with your json file:

Check the following places:

  • yourmodname.archive.xl:
    • Does the key localization - onscreens - en-us exist?
    • Is the indentation correct, as shown in the picture?
    • Does it point at the correct file (tutorial\ops\translation_strings.json), or did you rename or move it?
    • Did you make any typos?
  • yourModName.yaml:
    • Is the spelling for the key you defined after displayName and localizedDescription identical to the one in the json file?
  • translation_strings.json:
    • Is the spelling of the key defined in yaml's displayName and localizedDescription identical?
    • Did you set the femaleVariant (default)?

I spawn my item, but nothing happens!

That's relatively easy to fix — the error is somewhere in the first part of the chain:

Check the following places:

  • yourmodname.archive.xl:
    • Is the indentation correct, as shown in the picture?
    • Does the value for entityName have a corresponding entry in the factory (my_cool_new_items.csv)?
    • Does it point at the correct rootentity(tutorial\myshirt\rootentity.ent), or did you rename or move it?
    • Did you make any typos?
  • rootentity.ent:
    • Is the spelling for the key you defined after displayName and localizedDescription identical to the one in the json file?
    • Are you using any suffixes? Are you using the correct ones? Try creating a fall-back entry without any suffixes.

The item spawns, but…

Congratulations, you've made it into the right half of the diagram! The error will be somewhere here:

👉 If you set your mesh_entity.ent to point at a vanilla mesh, you can rule out your custom mesh and .mlsetup as a source of errors. Original game meshes will always have a working default appearance and will thus always be displayed!

The game crashes!

That means the chain is working, but something isn't loaded correctly. That's good! Check the following files:

  • appearance.app: Check the partsValues and partsOverrides entries. They need to point at the mesh_entity.ent, not at the mesh.
  • mesh_entity.ent: Does the component entry point to a valid mesh? Try it with a default mesh as detailed above.

If that works, then the problem is your mesh.

Troubleshooting a mesh

For more detailed error handling, check the sections below.

ℹ In the "Mesh Preview" tab of your mesh, you can "Generate Materials for Appearance". If the correct colours show up, you can at least rule out that the error is in the .*mesh or the *.mlsetup!

  • Make sure that you have the same number of entries in materialEntries and localMaterialBuffer.materials .
  • Go through the CMaterialInstances in localMaterialBuffer.materials.
    • Make sure that the files you're loading exist.
    • Make sure that you don't load a mlmask under a key for an mlsetup or vice versa.

If none of that helps, I suggest

  • doing a gdb export
  • throwing away your mesh (don't close the WKit tab yet), falling back to the original one
  • doing a gdb import
  • replacing the arrays appearances, localMaterialBuffer.materials and materialEntries with those from your previous mesh.

My mesh is black and shiny!

Congratulations, this is about the easiest-to-resolve error that you could've had. Your mesh is loaded correctly, there is only a problem with the rendered material.

Check your mesh file:

  • Check the connection between appearance, materialEntry and localMaterial.CMaterialInstance. Are the names correct?
  • Go through the CMaterialInstances in localMaterialBuffer.materials.
    • Make sure that the files you're loading exist.
    • Make sure that you don't load a mlmask under a key for an mlsetup or vice versa.

My mesh has the wrong appearance!

Either an appearance is incorrectly selected, or it is incorrectly resolved. Check the following places for copy-paste/duplication mistakes:

yourModName.yaml - is the appearanceName correct, or did you forget to change it?
rootentity.ent - does the name corresponding to the field above point to the appearanceName with the right name?
appearance.app - does the appearance's partOverride set the correct appearance in the componentsOverride?

Now, check the mesh file (close and re-open it to make everything refresh):

appearance - does it use the correctly named material?
materialEntries - is the index correct, or is it pointing at the index of the actually displayed material?
localMaterialBuffer - does the CMaterialInstance use the correct .mlsetup file?

Finally, check the .mlsetup: does it actually use different colours, or is it just a duplicate?

My mesh is invisible!

Here we go. This is the big one, and it's Not Fun. The error can be anywhere between the yaml and the mesh. You can rule out one of the files with the following question:

Does the glitching stop after <10 seconds?
If not: the appearance can't be resolved - ignore the mesh
If yes: the appearance is resolved, but can't be displayed - ignore the yaml

ℹIf the appearance is resolved, but not displayed (short glitch), the first thing you should do is to change the path in the mesh_entity.ent to one of the game's default meshes. This will rule out errors on your part. (Yes, even if your mesh worked in another mod. No, I'm not speaking from experience, why do you ask?)

If the hint above doesn't solve it, proceed to troubleshoot in the same way as "My mesh has the wrong appearance!" above.

I can't find anything, kill me now

Time to restore your files one by one to the last working backup and restart from there.
Don't delete them, keep them in a different folder - you will be able to copy a lot of stuff over.

ℹBy right-clicking on a tab title, you can move it to a new document group for easier copying.
Good luck, soldier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment