A Hat in Time Guide

Supporting Online Party Expressions for A Hat in Time

Supporting Online Party Expressions

Overview

Your expressions won’t work with OPE without some adjustments! Everything you need to know about setting them up can be found here.

The Gist

In case you’re here out of curiosity, Online Party Expressions (OPE) is a mod I made which synchronizes expressions across Online Party as best as it can. Since it looks at the expression currently equipped on the local player(s), OPE doesn’t need to know anything about the mod itself, which makes supporting OPE a straightforward process!

Expression components from the base game do not work when applied to online players. This is due to the GetOwnerMesh function of Hat_ExpressionComponent not being able to identify the mesh of online players.

Depending on what kind of expression component your mod is using, you will need to do a different process to get your expression component to work on online players. Online Party Plus (OPP) may also complicate the process.

Supported Base Game Expressions

If your expression component is one from the base game, e.g., not a custom one made by you, you might not have to do anything. The following expression components are already supported by OPE:

  • Hat Kid Hat_ExpressionComponent_HatKid
  • Bow Kid Hat_ExpressionComponent_BowKid
  • Shadow Puppet Hat_ExpressionComponent_ShadowPuppet

However, please note that there are special considerations for each of these if the expression component is applied to a different mesh than expected for an online player. Do note that these considerations may be bypassed using the replacement system (see respective section).

  • Hat Kid: For online players only:
    • For most cases, the mesh must use index 5 for the face and index 6 for the eyes.
    • If your mesh is a head mesh for OPP’s custom data system, then it must use index 0 for the face and index 4 for the eyes.

  • Bow Kid: For online players only:
    • For most cases, the mesh must use index 2 for the face and index 1 for the eyes.
    • If your mesh is a head mesh for OPP’s custom data system, then it must use index 1 for the face and index 0 for the eyes.

  • Shadow Puppet: For online players only, the mesh must use index 0 for the face and index 4 for the eyes.

As a side note, Wireframe (Hat Kid) Hat_Wireframe_ExpressionComponent_HatKid and Wireframe (Bow Kid) Hat_Wireframe_ExpressionComponent_BowKid are also technically supported, but because Wireframe normally does not sync, OPE translates each to its respective regular expression component (Hat_ExpressionComponent_HatKid or Hat_ExpressionCompoennt_BowKid). Because of this, I would highly recommend treating it as unsupported and reading the below section

Unsupported Base Game Expressions

All other base game expression components are not recognized by OPE. Due to this, they will not work on online players. If your mod uses such an expression component, then you will need to make a custom expression component.

This expression component can be as simple as extending the expression component you want to use, which will let it effectively be exactly the same expression component, but with any changes you want to make. That last part is important, because it needs to be made compatible with online players. Continue reading the below section to see what else needs to be done.

Custom Expressions

A custom expression component has the most liberty when it comes to supporting OPE. However, there are two things which must happen in order for it to correctly support OPE.

  1. The mesh for online players must have the same indices for the face and eyes as the one used for local players. OPE cannot tell the expression component to use different indices for online players.

    If this is impossible (such as if the mesh cannot be controlled), then you will need to use the replacement system. This may also be used to bypass this need even if the mesh can be controlled. See the below section.

  2. The expression component needs the issue with the GetOwnerMesh function fixed. Here, I have an override that should work for nearly anything!
    function MeshComponent GetOwnerMesh() { return Hat_GhostPartyPlayer(Owner) != None ? Hat_GhostPartyPlayer(Owner).SkeletalMeshComponent : Super.GetOwnerMesh(); }

The Replacement System

When using a custom expression component, there is no way for my mod to adjust the set indices for a different mesh. This is a problem because online players use different meshes for Hat Kid and Bow Kid.

To combat this, OPE has a special but more complicated system to allow mod creators to tell my mod to use a replacement expression component. This system supports being specified in custom expression components, skins, and playable characters, with minor differences in each implementation. Expression Components have priority over skins, which have priority over custom characters. You do not need to use all three methods.

The key method which is shared across all methods is telling OPE’s data collector (Ysm_Ope_CustomDataCollector) where your data is so that my mod can parse it.

For all methods

You will need to create a new class for each class that needs to use the system. This class may be named anything, but it must extend SeqVar_String. This class will eventually contain your custom data.

Because this is extending the class for a Kismet variable, it’ll try to show it as a valid Kismet variable in the A Hat in Time editor’s level Kismet editor. We can easily prevent this by overriding the following function as such.

static event bool IsValidLevelSequenceObject() { return false; }

Additionally, each custom data class will have a defaultproperties block that sets the StrValue property.

defaultproperties { StrValue= }

The custom data is a string (meaning it’s surrounded by double quotation marks) split into three sections.

  • ExprForSingle: The replacement expression component will be applied only when the online player is using a single-type mesh.
  • ExprForSingle: The replacement expression component will be applied only when the online player is using a split-type mesh (as per OPP’s definition).
  • ExprForBoth: The replacement expression component will be always be applied, regardless of mesh type.

Any section that isn’t needed can be omitted. Each section’s data is separated from other sections with a space. The information in each section is different depending on the method.

For each custom data class, you will want to have the following name prepared for it:

‘CustomDataCollection <Class Name>’

Replace <Class Name>, including the less than and greater than signs, with the name of the custom data class. Do not format it as class'<Class>’. Only include the name!

Expression Components

The data collector uses the GetOwnerMesh function of the expression component. The first thing the method should do before anything else is check if it’s attached to OPE’s custom data collector, and then set its Tag property to the string you’ve prepared.

if (Owner.IsA(‘Ysm_Ope_CustomDataCollector’)) { Owner.Tag = ‘CustomDataCollection <Class Name>’; return None; }

In the custom data class, you’ll set the StrValue for each section. The syntax for each section is as follows. Anything surrounded in less than and greater than signs should be replaced, including the signs.

<Section name> <Replacement Class>

Section name is the name of the section (such as ExprForSingle). <Replacement Class> is the name of the replacement expression component’s class. Do not format it as class'<Class>’. Only include the name!

The value should look similar to this:

StrValue=”ExprForSingle Mod_ExpressionComponent_MyCustom ExprForBoth Mod_ExpressionComponent_MyOtherCustom”

Skins

The data collector uses the Apply function of the expression component. The first thing the method should do before anything else is check if OPE’s custom data collector was passed as the argument (usually named a), and then set its Tag property to the string you’ve prepared.

if (a.IsA(‘Ysm_Ope_CustomDataCollector’)) { a.Tag = ‘CustomDataCollection <Class Name>’; return; }

In the custom data class, you’ll set the StrValue for each section. The syntax for each section is as follows. Anything surrounded in less than and greater than signs should be replaced, including the signs.

<Section name> <Local Class> <Replacement Class> <Local Class> <Replacement Class> …

Section name is the name of the section (such as ExprForSingle). <Local Class> is the class of the expression component applied to the local player of the online player. <Replacement Class> is the replacement expression component’s class. Do not format either class as class'<Class>’. Only include the name!

The value should look similar to this:

StrValue=”ExprForSplit Hat_ExpressionComponent_BowKid Mod_ExpressionComponent_MyCustom Mod_ExpressionComponent_MyCustomForMod ExprForSingle Hat_ExpressionComponent_HatKid Mod_ExpressionComponent_Wowza”

Custom Characters

The data collector uses the ConvertGhostPartyPlayer function of the expression component. The first thing the method should do before anything else is check if OPE’s custom data collector was passed as the argument (usually named gp), and then set its Tag property to the string you’ve prepared.

if (gp.IsA(‘Ysm_Ope_CustomDataCollector’)) { gp.Tag = ‘CustomDataCollection <Class Name>’; return; }

In the custom data class, you’ll set the StrValue for each section. The syntax for each section is as follows. Anything surrounded in less than and greater than signs should be replaced, including the signs.

<Section name> <Local Class> <Replacement Class> <Local Class> <Replacement Class> …

Section name is the name of the section (such as ExprForSingle). <Local Class> is the class of the expression component applied to the local player of the online player. <Replacement Class> is the replacement expression component’s class. Do not format either class as class'<Class>’. Only include the name!

The value should look similar to this:

StrValue=”ExprForBoth Hat_ExpressionComponent_BowKid Mod_ExpressionComponent_MyCustom Mod_ExpressionComponent_MyCustomForMod ExprForSplit Hat_ExpressionComponent_HatKid Mod_ExpressionComponent_Wowza”

Closing Words

While the above information should be everything needed to support OPE, if anything is still confusing, you may ask about it in the comments below, and I’ll try to clarify. If there’s anything that isn’t discussed that’s important for your expression component specifically, you may also ask about that.

Finally, if you’re concerned about if your changes are correct, OPE is set up in a way that allows local testing for many things! Assuming the expression component class is the same, you can run a cooked version of your mod without pushing an update, and the expression component should work if it’s been set up correctly. I highly encourage doing this to ensure that things are in order before pushing an update!

SteamSolo.com