Skip to content

Documentation

Nunchuk/ToasterOven edited this page Aug 9, 2024 · 4 revisions

Here I will document (probably) everything you can do when Importing Emotes.

Importing Animations

When Animation Plays

When Emote Spot Joined

BoneMapper Useful Info

CustomAnimationClip Useful Info

Possible use cases

Importing Animations

LethalEmotesAPI.ImportV2.EmoteImporter.ImportEmote

ImportEmote takes in a CustomEmoteParams, which contains all the values for your animation, it has the following

public class CustomEmoteParams
{
    /// <summary>
    /// List of primary animation clips, one of these will be picked randomly if you include more than one
    /// </summary>
    public AnimationClip[] primaryAnimationClips;

    /// <summary>
    /// List of secondary animation clips, must be the same size as primaryAnimationClips, will be picked randomly in the same slot (so if we pick primaryAnimationClips[3] for an animation, we will also pick secondaryAnimationClips[3])
    /// </summary>
    public AnimationClip[] secondaryAnimationClips = null;

    /// <summary>
    /// Used to specify if audio is looping, you only need to set this if you are importing primary audio files, if you use secondaryAudioClips, this does nothing
    /// </summary>
    public bool audioLoops;

    /// <summary>
    /// Primary list of audio clips
    /// </summary>
    public AudioClip[] primaryAudioClips = null;

    /// <summary>
    /// Secondary list of audio clips, if these are specified, the primary clip will never loop and the secondary clip that plays will always loop
    /// </summary>
    public AudioClip[] secondaryAudioClips = null;

    /// <summary>
    /// Same as primaryAudioClips but will be played if DMCA settings allow it (if normal audio clips exist, and dmca clips do not, the dmca clips will simply be silence)
    /// </summary>
    public AudioClip[] primaryDMCAFreeAudioClips = null;

    /// <summary>
    /// Same as secondaryAudioClips but will be played if DMCA settings allow it
    /// </summary>
    public AudioClip[] secondaryDMCAFreeAudioClips = null;

    /// <summary>
    /// If true, will turn off the emote when the player starts moving, to prevent movement entirely use preventMovement
    /// </summary>
    public bool stopWhenMove = false;

    /// <summary>
    /// If true, will prevent movement actions while emote is playing, to stop the emote when the player moves use stopWhenMove
    /// </summary>
    public bool preventMovement = false;

    /// <summary>
    /// If false, will hide the emote from all normal areas, however it can still be invoked through PlayAnimation, use this for emotes that are only needed in code
    /// </summary>
    public bool visible = true;

    /// <summary>
    /// If true, will sync animation between all people emoting
    /// </summary>
    public bool syncAnim = false;

    /// <summary>
    /// If true, will sync audio between all people emoting
    /// </summary>
    public bool syncAudio = false;

    /// <summary>
    /// Spot in primaryAnimationClips array where a BoneMapper will play when there is no other instance of said emote playing -1 is random, -2 is sequential, anything else is what you make it to be
    /// </summary>
    public int startPref = -1;

    /// <summary>
    /// Spot in primaryAnimationClips array where a BoneMapper will play when there is at least one other instance of said emote playing, -1 is random, -2 is sequential, anything else is what you make it to be
    /// </summary>
    public int joinPref = -1;

    /// <summary>
    /// Array of join spots which will appear when the animation is playing
    /// </summary>
    public JoinSpot[] joinSpots = null;

    /// <summary>
    /// Use this if you want a custom internal name for the emote, if not specified, will auto create it based off of the first item in primaryAnimationClips
    /// </summary>
    public string internalName = "";

    /// <summary>
    /// Changes the user facing name, you should use this, can have duplicates
    /// </summary>
    public string displayName = "";

    /// <summary>
    /// If declared, will fire when an emote plays audio with sync enabled
    /// </summary>
    public Action<BoneMapper> customPostEventCodeSync = null;

    /// <summary>
    /// If declared, will fire when an emote plays audio with sync disabled
    /// </summary>
    public Action<BoneMapper> customPostEventCodeNoSync = null;

    /// <summary>
    /// Determines the lock type of your emote, none, headBobbing, lockHead, or rootMotion
    /// </summary>
    public LockType lockType = LockType.none;

    /// <summary>
    /// Lets you mark if your normal set of audio will get claimed by DMCA
    /// </summary>
    public bool willGetClaimedByDMCA = true;

    /// <summary>
    /// Determines the volume of the emote in terms of alerting enemies, 0 is nothing, 1 is max
    /// </summary>
    public float audioLevel = .5f;

    /// <summary>
    /// If true, will default animation to third person, although there are user settings to override this in either direction
    /// </summary>
    public bool thirdPerson = false;

    /// <summary>
    /// Will ignore any bones declared on this list and all of their child bones (recursive) from the animation that plays
    /// </summary>
    public HumanBodyBones[] rootBonesToIgnore = null;

    /// <summary>
    /// Will ignore any bones declared on this list from the animation that plays
    /// </summary>
    public HumanBodyBones[] soloBonesToIgnore = null;

    /// <summary>
    /// Will use local positions instead of global positions, used primarily with root/soloBonesToIgnore
    /// </summary>
    public bool useLocalTransforms = false;

    /// <summary>
    /// <see cref="BepInPlugin"/> of the mod that created this <see cref="AnimationClipParams"/>.
    /// </summary>
    public BepInPlugin OwnerPlugin { get; } = Assembly.GetCallingAssembly().GetBepInPlugin();

    /// <summary>
    /// Declare if your emote will not directly call animation. AnimationClips passed into earlier parameters will still be used for preview purposes, leave those null if not needed.
    /// </summary>
    public bool nonAnimatingEmote = false;

    /// <summary>
    /// Determines if the emote can be joined by pressing the join key. Mainly only want to turn this off for specific emote types.
    /// </summary>
    public bool allowJoining = true;

    /// <summary>
    /// If setup, the specified emote will be played when joining instead of the currently playing emote. I.E: this emote is called Emote1, but your emoteToPlayOnJoin is Emote2. If a player joins this emote they will instead play Emote2
    /// </summary>
    public string emoteToPlayOnJoin = "";

    /// <summary>
    /// If true, will automatically switch the camera to third or first person based off of the thirdPerson bool.
    /// </summary>
    public bool forceCameraMode = false;

    /// <summary>
    /// If set to false, prevents third person from ever enabling during this emote
    /// </summary>
    public bool allowThirdPerson = true;

    /// <summary>
    /// Whether or not the healthbar should animate when using this emote
    /// </summary>
    public bool animateHealthbar = true;
}
JoinSpot
  • string _name (Name of join spot)
  • Vector3 _position (Position of join spot relative to BoneMapper)
  • Vector3 _rotation (Rotation of join spot relative to BoneMapper)
  • Vector3 _scale (Scale of join spot relative to BoneMapper)

When Animation Plays

CustomEmotesAPI.animChanged
  • string newAnimation (The name of the animation that played, "none" is well, none)
  • BoneMapper mapper (The BoneMapper that played said animation)

When Emote Spot Joined

CustomEmotesAPI.emoteSpotJoined_Body
  • GameObject emoteSpot (The emoteSpot that has been joined, you can get the name and such from this. Emotespots always have an EmoteLocation component attached)
  • BoneMapper joiner (The BoneMapper which joined the emoteSpot)
  • BoneMapper host (The BoneMapper which owns the emoteSpot)
CustomEmotesAPI.emoteSpotJoined_Prop
  • Same as above, but for internal reasons they are separate calls.
EmoteLocation
  • SetEmoterAndHideLocation(BoneMapper boneMapper) (If you call this, it will hide the emote spot until said BoneMapper changes animations)

BoneMapper Useful Info

  • public static Dictionary<string, CustomAnimationClip> animClips (List of all CustomAnimationClips)
  • public CustomAnimationClip currentClip = null (current CustomAnimationClip)
  • public CustomAnimationClip prevClip = null (previous CustomAnimationClip)
  • public bool local = false (Whether or not BoneMapper is the local user)
  • public List<GameObject> props (Props which you can assign to a BoneMapper, deleted every time a new animation is played, unless...)
  • public bool preserveProps = false (If true, prevents props from being cleared at the start of a new animation, then resets back to false)
  • public float autoWalkSpeed = 0 can allow your emotes to auto walk the player forward/backwards (-1 - 1)
  • public GameObject currentEmoteSpot = null (The current emote spot (if any) that is being used by the BoneMapper)
  • public bool worldProp = false (Whether or not the BoneMapper is a world prop)
  • public GameObject parentGameObject (For some animations, you might want to lock a character to a specific gameobject, providing a ton more options for movement, this is automatically removed at the start of any animation unless...)
  • public bool preserveParent = false (if set to true, switches back to false and prevents the parentGameObject from being decoupled)
BoneMapper.AssignParentGameObject
  • GameObject youAreTheFather (The GameObject you are assigning as the parent. Unless you are using this in combination with preserveParent, it is recommended to also add this GameObject to the BoneMapper's props)
  • bool lockPosition (Locks position of the character to youAreTheFather)
  • bool lockRotation (Locks rotation of the character to youAreTheFather)
  • bool lockScale (Locks scale of the character to youAreTheFather)
  • bool scaleAsScavenger = true (Only relevant if lockScale is on, uses the scale value from earlier to scale the character as close to the base scavenger's height as possible)
  • bool disableCollider = true (Disables the character's physics collider (not hurtbox) until the parent is unassigned, this prevents clipping issues if you lock two characters in the same spot)
BoneMapper.ScaleProps
  • Uses the scale value from earlier to scale any assigned props up or down to the character's size.
BoneMapper.SetAnimationSpeed
  • float speed (Sets the animator speed, reset to 1 whenever an emote plays so set it after you start an animation)
BoneMapper.SetAutoWalk
  • float speed (sets autoWalkSpeed)
  • bool overrideBaseMovement (if true, "speed" will instead be a multiplier of the movement input from the player (if the player held W, so .5, they would instead go 1 if the speed is set to 2))
  • bool autoWalk (sets autoWalk
  • Just a helper function, not much to see here

CustomAnimationClip Useful Info

  • public int syncPos (Sync position used in the following two items, however do note, most of this is done automatically and there are only a couple cases where you will want to mess with this, but they do exist)
  • public static List<float> syncTimer (Sync timer for animation's playing)
  • public static List<int> syncPlayerCount (How many current characters are performing an animation)

Possible Use Cases

  • If you are importing an EmoteSkeleton, consider subscribing to the animChanged event, and check if the BoneMapper.name is equal to the emoteskeleton you imported, then if true, also check if the played animation is "none"/not "none". Depending on the outcome, you can set it to hide weapon meshes on your character while they animate.
  • If you have multiple SEPERATE emotes but still want them all to sync together, consider keeping track of the syncPos of the first CustomAnimationClip you setup and then applying it to all other animations you want to sync up. You could grab it by calling something along the lines of: int syncpos = BoneMapper.animClips[emoteName].syncPos
  • If you join at a join spot, you will probably want to lock your position in with the host. Consider setting up a listener on emoteSpotJoined_Body. If the emotespot's name is equal to whatever you called it, create a new GameObject, we'll call it G. First off, play whatever animation is required, then assign G to the joinerMapper's props. Set G's parent to the hostMapper's transform and setup G's local transform data to be correct, possible all Vector3.zero. Finally on joinerMapper, call AssignParentGameObject with G as the parent.
  • If you subscribe to animChanged, you can check if the newAnimation is equal to "AutoWalkAnimation" (just for example, this isn't a real animation) and if so, call SetAutoWalk on the BoneMapper.