Dynamic appearances: Understanding the process
How does the ancient magic work?
Summary
This page explains the files from ItemAdditions: Dynamic Appearances -> Generated files, what they do, and how they do it.
The yaml
For a general explanation of what the yaml file does, check The control file: yourModName.yaml. This section will only cover the differences between a dynamic and a regular yaml.
This is your dynamic project's yaml file, minus any properties that aren't influenced by the dynamic appearances:
Items.manavortex_my_custom_shirt_$(base_color)_$(secondary):
$base: Items.TShirt
$instances:
- {base_color: black, secondary: samurai}
- {base_color: black, secondary: witcher}
- {base_color: black, secondary: galaxy}
- {base_color: white, secondary: samurai}
- {base_color: white, secondary: witcher}
- {base_color: white, secondary: galaxy}
- {base_color: red, secondary: samurai}
- {base_color: red, secondary: witcher}
- {base_color: red, secondary: galaxy}
appearanceName: my_custom_shirt_!$(base_color)+$(secondary)
entityName: my_custom_shirt_factory_name
localizedDescription: LocKey#my_custom_shirt_i18n_desc
displayName: LocKey#my_custom_shirt_i18n_$(base_color)_$(secondary)
quality: Quality.Legendary
icon:
atlasResourcePath: manavortex\my_archive_xl_item\my_custom_shirt\my_custom_shirt_icons.inkatlas
atlasPartName: my_custom_shirt_$(base_color)_$(secondary)
statModifiers:
- !append Quality.IconicItem
- !append Character.ScaleToPlayerLevel
appearanceSuffixes: []
statModifierGroups:
- !append-once Items.IconicQualityRandomization
placementSlots: OutfitSlots.TorsoInner
This section will explain how that works - except for the appearanceName, you will find that in The root_entity.
Record names
TweakXL will generate one record per entry in $instances, according to the rules that you're using in the item name. This happens via property interpolation.
The example above will generate three item entries by substituting $(property_name) with the value of the property from the entry. If that isn't clear enough, check the example and the resulting item codes at the end of the line.
Items.manavortex_my_custom_shirt_$(base_color)_$(secondary):
$instances:
- {base_color: black, secondary: samurai} # Items.manavortex_my_custom_shirt_black_samurai
- {base_color: black, secondary: witcher} # Items.manavortex_my_custom_shirt_black_witcher
- {base_color: black, secondary: galaxy} # Items.manavortex_my_custom_shirt_black_galaxy
- {base_color: white, secondary: samurai} # Items.manavortex_my_custom_shirt_white_samurai
- {base_color: white, secondary: witcher} # Items.manavortex_my_custom_shirt_white_witcher
- {base_color: white, secondary: galaxy} # Items.manavortex_my_custom_shirt_white_galaxy
- {base_color: red, secondary: samurai} # Items.manavortex_my_custom_shirt_red_samurai
- {base_color: red, secondary: witcher} # Items.manavortex_my_custom_shirt_red_witcher
- {base_color: red, secondary: galaxy} # Items.manavortex_my_custom_shirt_red_galaxyIf you install and launch your project, you can immediately spawn them in Cyberpunk via Cyber Engine Tweaks:
Game.AddToInventory("Items.manavortex_my_custom_shirt_black_samurai")
Game.AddToInventory("Items.manavortex_my_custom_shirt_black_witcher")
Game.AddToInventory("Items.manavortex_my_custom_shirt_black_galaxy")
Game.AddToInventory("Items.manavortex_my_custom_shirt_white_samurai")
Game.AddToInventory("Items.manavortex_my_custom_shirt_white_witcher")
Game.AddToInventory("Items.manavortex_my_custom_shirt_white_galaxy")
Game.AddToInventory("Items.manavortex_my_custom_shirt_red_samurai")
Game.AddToInventory("Items.manavortex_my_custom_shirt_red_witcher")
Game.AddToInventory("Items.manavortex_my_custom_shirt_red_galaxy")Display names
Like the record names, the displayName property is also generated for each entry:
displayName: my_custom_shirt_i18n_$(base_color)_$(secondary)
$instances:
- {base_color: black, secondary: samurai} # my_custom_shirt_i18n_black_samurai
- {base_color: black, secondary: witcher} # my_custom_shirt_i18n_black_witcher
- {base_color: black, secondary: galaxy} # my_custom_shirt_i18n_black_galaxy
- {base_color: white, secondary: samurai} # my_custom_shirt_i18n_white_samurai
- {base_color: white, secondary: witcher} # my_custom_shirt_i18n_white_witcher
- {base_color: white, secondary: galaxy} # my_custom_shirt_i18n_white_galaxy
- {base_color: red, secondary: samurai} # my_custom_shirt_i18n_red_samurai
- {base_color: red, secondary: witcher} # my_custom_shirt_i18n_red_witcherThese entries have been auto-generated in your localization file.
Icons
The icon name in the record is also generated for each entry. They are all using the same inkatlas, but you can generate that as well if you want - I've done it for the Netrunner suits, since I needed more than 100 icons.
icon:
atlasResourcePath: manavortex\my_archive_xl_item\my_custom_shirt\my_custom_shirt_icons.inkatlas
atlasPartName: my_custom_shirt_$(base_color)_$(secondary)
$instances:
- {base_color: black, secondary: samurai} # my_custom_shirt_i18n_black_samurai
- {base_color: black, secondary: witcher} # my_custom_shirt_i18n_black_witcher
- {base_color: black, secondary: galaxy} # my_custom_shirt_i18n_black_galaxy
- {base_color: white, secondary: samurai} # my_custom_shirt_i18n_white_samurai
- {base_color: white, secondary: witcher} # my_custom_shirt_i18n_white_witcher
- {base_color: white, secondary: galaxy} # my_custom_shirt_i18n_white_galaxy
- {base_color: red, secondary: samurai} # my_custom_shirt_i18n_red_samurai
- {base_color: red, secondary: witcher} # my_custom_shirt_i18n_red_witcherYou can see that the generated names are identical to the ones in the section Display names above. That is because the key to generate them (atlasPartName) has the same pattern as displayName .
The root_entity
For a general explanation of the root entity, check root_entity.ent. This section will only cover the differences between a dynamic and a regular root entity.

DynamicAppearance, without S.For dynamic appearances, your root_entity file will contain one entry. You can give every item its own root entity, or share a file across the mod.
The appearance name in the root entity corresponds to the appearanceName property in the .yaml without the variant:

You can leave the appearanceName blank. In that case, ArchiveXL will look for an appearance with the same name as the name attribute.
The .app
For a general explanation of the .app file, check appearance.app. This section will only cover the differences between a dynamic and a regular .app file.
For dynamic variants, components in the .app file will be ignored. You have to use a mesh entity.
.app file: Conditional switching
You can define appearances for different circumstances by changing the appearance names. This will let you influence the mesh entity even further by e.g. hiding parts of the mesh via chunkMask. And the best part is: you don't even need to touch your root entity.
In the context of our example project, this means that you can define your appearances like this:
app_file_dynamic_appearance
Your regular appearance. Is displayed when none of the conditional ones apply.
app_file_dynamic_appearance&camera=tpp
This is only active in third person perspective. The item will be completely invisible in first person.
app_file_dynamic_appearance&camera=fpp
This becomes active whenever you are in first person perspective. You'll usually want this to remove the mesh from your face via partsOverrides.
app_file_dynamic_appearance&gender=male
You shouldn't do this — instead, use Substitutions in the mesh file path.
The mesh_entity
Unless you are using .app file: Conditional switching, this file contains the entire magic. This section will explain how you tell the .ent file to load different meshes.
You can use either Substitutions or (not recommended) .ent file: conditional switching.
Wait, this is not what I want!
For a picture of the difference between substitutions and conditional switching, check The diagram 's bottom left corner
For a general explanation of the mesh entity, check mesh_entity.ent
To learn more about dynamic appearances, check the ArchiveXL documentation
How it works
Per default, all components in the mesh entity are active and will be added to the player when you equip your item. Which parts of them are visible is determined by their chunk masks (set everything to visible, then use the .app's partsOverride to hide parts of them).
Substitutions
Substitutions in path names will be switched out by ArchiveXL at runtime. For example, p{gender}a becomes pwa if your V has a female body gender, and pma if they don't.
To enable substitution, your depot path must begin with an asterisk *. Each substitution needs to be enclosed in braces, e.g. {gender}.
All generated paths will already be dynamic.

.ent file: conditional switching
This is only documented because it's possible, not because it's good practice: ArchiveXL will create all components, hiding those that aren't matched by your current conditions.
If possible, use Substitutions together with .app file: Conditional switching and PartsOverrides: Changing other meshes.
The diagram
Now let's look at what we just did and check the diagram. You'll see that the control files are almost identical to the vanilla variants, but that the rest of the files has gotten a lot more manageable:

Last updated

