ArchiveXL: Dynamic conversion guide

How to convert your mod to use dynamic variants... in ten minutes

Summary

Published: June 30 2024 by manavortex Last documented update: June 30 2024 by manavortex

This page will teach you how to convert your existing ArchiveXL mod to the "new" system.

Prerequisites

  • An ArchiveXL mod that you want to convert

We will go through the process step-by-step. I'll explain what I'm doing as I go along.

Step 0: The .yaml

Instead of one entry per item variant, we only need one entry per item.

Our variants are defined via $instances and generated via substitutions. Let's take a look at this:

You'll notice that the displayNames are different now. I could prevent that by writing my_shirt_localization_name_$(shirt)_$(ribbons), but this is a great chance to clean up the historically-grown mess.

What did we do here?

  • We deleted the appearanceSuffixes (we'd only need them for Gendered preview icons, and we can't be arsed with that)

  • We defined a list of $instances, which will generate all of our different appearances

  • We replaced all parts that are different with the corresponding key from $instances. The rest will be magic.

And that !+ stuff?

Defines the variants — you'll get a gold star for paying attention, but we're getting to that later (Step 3: The mesh entity)

Step 1: The root entity

Instead of one entry per variant and gender, we only need one entry in total.

Our appearance's name must still match the .yaml, but only up to the variant separator: we're going with appearance_root_entity_.

By leaving the appearanceName blank, we're telling ArchiveXL to re-use name. If you want to use a specific appearance name (or if you don't want appearance_root_entity_ in your .app), you can put something different here.

What did we do here?

  • We added the DynamicAppearance tag to enable the feature

  • We deleted all appearances but one

  • We deleted everything variant-related from the appearance

  • We deleted the appearanceName, so that ArchiveXL will re-use name for the .app

Step 2: The .app

Instead of one appearance per variant and gender, we will squash everything down so that we have only one appearance per gender. In Let's simplify this even further., I will teach you how to reduce that even further, but for now, we'll be going step by step.

What did we do here?

  • We deleted all but one appearance per gender

  • We added the condition &gender=w and &gender=m

  • We deleted the partsOverrides

Gender matching now happens via condition. The different meshes for male and female body gender are loaded in the different mesh entity files.

For a full list of conditions, see ArchiveXL: Suffixes and Substitutions

But that will always load the default appearance!

Right! You've been paying attention! Let's take a look at that next.

Step 3: The mesh entity

We will only change a single field (meshAppearance).

Before, it was mesh_white_red (the default appearance), and each appearance overwrote it via partsOverride. We're now changing it to dynamic:

Old: mesh_white_red New: *mesh_{variant.2}_{variant.1}

This is where file validation currently stops working (as of June 2024). If you make any mistakes here, you will always see the first mesh appearance.

What did we just do?

  • By adding a leading *, we told ArchiveXL that this field is involved in magic (substitution in this case)

  • By replacing white with {variant.2}, we're using the second parameter from the yaml's appearanceName (shirt).

  • By replacing red with {variant.1}, we're using the first parameter from the yaml's appearanceName (ribbons).

Parameters? Variants? What?

Let's unpack this.

In this example, we have the following mesh appearances:

  • mesh_white_red

  • mesh_black_red

  • mesh_black_blue

By using ! in the appearanceName, we're telling ArchiveXL that everything after is part of the mesh appearance definition:

This is the reason why we've used only appearance_root_entity_ as the appearanceName, and not the whole ordeal.

The + in the variant splits it up. If we didn't have it, we couldn't use variant_1 and variant_2.

How would that look?

Let's compare our setup ( *mesh_{variant.2}_{variant.1}) to the alternative.

yaml:

appearanceName: appearance_root_entity!$(shirt)_$(ribbons)

Our meshAppearance would be *mesh_{variant}.

Without the split, we need to make sure to pass the variant exactly as expected.

Recap

Unless you got hopelessly lost, this should have taken no more than ten minutes, and your mod just works(tm).

You now have one root_entity entry, two appearanceAppearanceDefinitions in your .app, and one mesh_entity per body gender.

Let's simplify this even further.

Currently, we have two mesh_entities:

tutorial\torso\my_custom_shirt\mesh_entity_pwa.ent
tutorial\torso\my_custom_shirt\mesh_entity_pma.ent

Each of them points at a different mesh:

tutorial\torso\my_custom_shirt\meshes\t1_custom_shirt_pwa.mesh
tutorial\torso\my_custom_shirt\meshes\t1_custom_shirt_pma.mesh

Delete one of the mesh entities and rename the other:

tutorial\torso\my_custom_shirt\mesh_entity.ent

The .app file

Delete one of your appearances, and strip the suffix name from the other, leaving only appearance_root_entity_.

Change the path of your partsValue to the only remaining mesh entity, and save the file.

This will only load one body gender (the one you defined in the mesh_entity).

The mesh_entity: Round 2

Method 1: Conditional Components

You should use the other approach where possible; this is only documented here for the sake of completeness.

Just like with appearances, you can also use Conditions with component names.

Pro: Especially with many body mods, you can just put all the logic into the components

Cons: All of these components will be added to the player entity, even if half of them will be hidden. This is bad for performance, especially if everyone does it.

If you want several different body mods or feet state to use the same mesh, you can use ArchiveXL: Resource linking instead.

Method 2: Component substitution

The best way of dealing with multiple meshes is to have a single component and let ArchiveXL handle all the heavy lifting by using substitutions in the path:

This is our mesh's depot path:

*tutorial\torso\my_custom_shirt\meshes\t1_custom_shirt_p{gender}a.mesh

A combination of

Last updated