Decal Registry - EverestAPI/Resources GitHub Wiki

Some decals in the game have hardcoded properties which cannot be replicated normally with custom decals. This is where the Decal Registry is useful.

To start using it, first create a DecalRegistry.xml file in your Mod's root (next to the everest.yaml). An example file:

<decals>
  <decal path="frosttemple/icegrass/grass_a">
    <banner amplitude="2" sliceSize="1" speed="2" sliceSinIncrement="0.05" easeDown="false" offset="-2" />
  </decal>
</decals>

You can add as many <decal> tags as you want. The path attribute of <decal> is the path to your decal sprite relative to Graphics/Atlases/Gameplay/decals/ (for an animated decal, use the name of the texture without the frame number). Inside of a <decal>, you can specify as many properties as you want, although not all of them are compatible with each other. If you run into a compatibility issue that you would like addressed, reach out an Everest Team member.

Most properties have attributes which let you configure them. Some attributes must be included for the property to be applied, but others are optional and will be given default values if not included. Note: Properties need to be closed. For example <floaty/>, not <floaty>.

Each attribute has a data type that defines what values you can use:

  • string - a sequence of characters, e.g. name="mystring"
  • int - a non-decimal number, e.g. offsetX="-3"
  • float - a decimal number, e.g. speed="0.4"
  • bool - a value that must be true or false, e.g. safe="true"
  • frames - a list of frames using sprite animation format, e.g. frames="0,1,3-5,6*3" or frames="0,1,3,4,5,6,6,6" (equivalent)

Everest currently supports these properties:

  • <animation> Makes a decal play a looped animation.
    • frames(frames): the animation to play (default: "0").
  • <banner> Makes the decal wave left-to-right in a sine 🔗 pattern. The effect divides the decal into horizontal "slices" and then moves each slice to make the entire decal look like it's waving. Used in vanilla for curtains, flowers, grass, etc.
    • speed(float) - the speed of the wave (default: 1.0).
    • amplitude(float) - the peak of the wave at its strongest point (default: 1.0).
    • sliceSize(int) - the height of each slice in pixels (default: 1).
    • sliceSinIncrement(float) - the amount to progress the wave function for each slice in radians 🔗 (default: 1.0). Keep small for a smoother wave, e.g. 0.05.
    • easeDown(bool) - whether or not the strength of the wave should increase from the top-down instead of from the bottom-up (default: false).
    • offset(float) - the amount to shift the wave function in pixels (default: 0).
    • onlyIfWindy(bool): whether or not the decal should only wave when the wind is active (default: false).
  • <floaty> Makes the decal floaty. No attributes. Used in vanilla for the floating background Farewell decals.
  • <smoke> Makes the decal emit smoke particles. Used in vanilla for vents, chimneys, etc.
    • offsetX(float) and offsetY(float) - the offset of the emitter from the center of the decal in pixels (default: 1.0).
    • inbg(bool) - whether or not to emit the particles in the background instead of the foreground (default: false).
    • This property can be applied to a decal multiple times.
  • <parallax> Adds parallax to the decal. This shifts the decal's position as the camera moves to make it appear closer or farther from the screen.
    • amount(float) - the amount of parallax to apply. Use positive values for closer, negative for farther. For reference, Summit clouds use 0.1.
  • <depth> Sets the Depth of the decal, allowing it to show up in front of or behind specific objects.
    • value(int) - the depth value. See the table below for reference.
Click to expand Table of Depths
BGTerrain = 10000
BGMirrors = 9500
BGDecals = 9000
BGParticles = 8000
SolidsBelow = 5000
Below = 2000
NPCs = 1000
TheoCrystal = 100
Player = 0
Dust = -50
Pickups = -100
Seeker = -200
Particles = -8000
Above = -8500
Spinners = -8500
Solids = -9000
FGTerrain = -10000
FGDecals = -10500
DreamBlocks = -11000
PlayerDreamDashing = -12000
Enemy = -12500
FakeWalls = -13000
FGParticles = -50000
Top = -1000000
FormationSequences = -2000000
  • <animationSpeed> Sets the animation speed of the decal.
    • value(int) - the animation speed in frames-per-second (normally 12).
  • <sound> Adds a sound source to the decal.
    • event(string) - the name of the event to play.
  • <bloom> Adds a bloom point to the decal.
    • offsetX(float) and offsetY(float) - the offset of the bloom from the center of the decal in pixels (default: 0).
    • alpha(float) - the transparency of the bloom, where 0 is fully transparent and 1.0 is fully opaque (default: 1.0).
    • radius(float) - the radius of the bloom in pixels (default: 1.0).
    • This property can be applied to a decal multiple times.
  • <coreSwap> Uses a different texture path for the decal depending on the core mode.
    • coldPath(string) - the path to use for cold mode.
    • hotPath(string) - the path to use for hot mode. Note: both of these paths are relative to the Gameplay atlas, not the decals folder, so the paths have to be prefixed with decals/ if the textures are placed in that folder.
  • <flagSwap> Uses a different texture path for the decal depending on the state of a specified flag.
    • flag(string) - the flag used to determine the state.
    • offPath(string) - the path to use when the flag is off.
    • onPath(string) - the path to use when the flag is on. Note: both of these paths are relative to the Gameplay atlas, not the decals folder, so the paths have to be prefixed with decals/ if the textures are placed in that folder.
  • <mirror> Adds a reflection to the decal. Used in vanilla for the mirror decals in 5A.
    • keepOffsetsClose(bool) - whether or not the reflection should appear slightly closer to the player (default: false).
    • To use, create a folder named mirrormasks on the same level as your decals folder, and create all the subfolders corresponding to the decal's original file path. Create a texture file with the same name as the original decal in this path (for example, decals/modname/mytexture.png will have a corresponding mirrormasks/modname/mytexture.png texture file).
    • The red channel controls the horizontal offset of the reflection, and the green channel controls the vertical offset (the blue channel is unused). So 255 red and 255 green makes it the closest to the player, 0 red and 255 green makes it furthest horizontally, 255 red and 0 green makes it furthest vertically, and 0 red and 0 green makes it the furthest in both axis. Transparent pixels do not cast reflections.
  • <solid> Adds an invisible solid block to the decal. Used in vanilla for the resort roof decals.
    • x(float) and y(float) - the top-left corner of the solid relative to the center of the decal (default: 0).
    • width(float) and height(float) - the dimensions of the solid (default: 16).
    • index(int) - the sound surface index to use for e.g. footstep sounds (default: 14 [Resort Roof]). See the table below for reference.
    • blockWaterfalls(bool) - whether or not this surface blocks waterfalls (default: true).
    • safe(bool) - whether or not berries will collect on this surface (default: true).
    • This property can be applied to a decal multiple times.
Click to expand Table of Sound Surface Indexes
None = 0
Asphalt = 1
Car = 2
Dirt = 3
Snow = 4
Wood = 5
Bridge = 6
Girder = 7
Brick = 8
Zip Mover = 9
Space Jam (Inactive) = 11
Space Jam (Active) = 12
Resort Wood = 13
Resort Roof = 14
Resort Platform = 15
Resort Basement = 16
Resort Laundry = 17
Resort Boxes = 18
Resort Books = 19
Resort Forcefield = 20
Resort Clutterswitch = 21
Resort Elevator = 22
Cliffside Snow = 23
Cliffside Grass = 25
Cliffside Whiteblock = 27
Gondola = 28
Glass = 32
Grass = 33
Cassette Block = 35
Core Ice = 36
Core Rock = 37
Glitch = 40
Internet Café = 42
Cloud = 43
Moon = 44
  • <staticMover> Attaches this decal to solid entities within the relative collision box described by the attributes.
    • x(int) and y(int) - the top-left corner of the static mover relative to the center of the decal (default: 0).
    • width(int) and height(int) - the dimensions of the static mover (default: 16).
    • Note that some solid entities, e.g. crumble blocks, may not work with static mover decals.
    • Static mover decals attached to persistent solids like dash blocks will get removed when they do.
  • <scared> Makes a decal play certain animations depending on how close a player is to it. Used for certain plants in Farewell.
    • hideRange(int) - if the player gets within hideRange pixels, "hide" the decal (default: 32).
    • showRange(int) - if the player moves further away than showRange pixels while we're hiding, "show" the decal (default: 48).
    • idleFrames(frames): the animation that loops while we're not hiding (default: "0").
    • hideFrames(frames): the animation that plays when the player gets within hideRange (default: "0").
    • hiddenFrames(frames): the animation that loops while we're hiding (default: "0").
    • showFrames(frames): the animation that plays when the player moves further than showRange (default: "0").
    • You can optionally use range(int) instead of hideRange and showRange if you want them to be the same value.
  • <light> Adds a vertex light to the decal.
    • offsetX(float) and offsetY(float) - the offset of the light from the center of the decal in pixels (default: 0).
    • color(string) - the color of the light as a hex string (default: ffffff which converts to white).
    • alpha(float) - the transparency of the light, where 0 is fully transparent and 1.0 is fully opaque (default: 1.0)
    • The alpha of the light will start to decrease at a radius of startFade(int) (default: 16) and will reach 0 at a radius of endFade(int) (default: 24).
    • This property can be applied to a decal multiple times.
  • <lightOcclude> Adds a light-blocking component to the decal in the shape of the rectangle described by the attributes.
    • x(int) and y(int) - the top-left corner of the occluder relative to the center of the decal (default: 0).
    • width(int) and height(int) - the dimensions of the occluder (default: 16).
    • alpha(float) - the occluder transparency, where 0 blocks nothing and 1.0 blocks all light (default: 1.0).
    • This property can be applied to a decal multiple times.
  • <overlay> Overrides a tile entity's texture with the decal's texture (including transparency) where they overlap. Equivalent to object tiles. Allows you to e.g. add a "crack" decal to the edge of a dash block to indicate a secret.
    • No attributes. Ignores all other properties.
    • Supports any tile entity with a Tile Interceptor component. Supported vanilla entities are: Coverup Wall, Crumble Wall On Rumble, Dash Block, Exit Block, Fake Wall, Falling Block, Badeline Boss Moving Block, and Intro Crusher.
  • <scale> Multiplies the scale of the decal by the values provided. Applies after any manual scale set in Ahorn or Loenn.
    • multiplyX(float) and multiplyY(float) - the values to multiply the decal scale by (default: 1.0).
    • Other Everest properties will automatically be adjusted based on the decal scale where necessary (e.g. offsets).
  • <randomizeFrame> Chooses a random starting frame for animated decals. No attributes.

Applying properties to multiple decals at once

If you need have multiple decals in your mod that need the same properties, you can apply properties to all of them, without having to duplicate the same <decal> element. There are two ways to do so:

  • Using / at the end of the path attribute will select every decal that is located in the specified directory. Example:

    <decal path="catapillie/clouds/">

    This will affect: catapillie/clouds/big, catapillie/clouds/small, etc... But will not affect: catapillie/clouds/blue/big, etc...

  • Using * at the end of the path attribute will select every decal whose path starts with the specified one, only if they are located in the same directory. Example:

    <decal path="catapillie/cloud*">

    This will affect: catapillie/cloud_a, catapillie/cloudB, etc... But will not affect: catapillie/cloud, catapillie/clouds/custom, etc...

Note that <decal> elements are sorted before being applied depending on their type of selection, and not in order inside the file. The order is:

  • Folders (/)
  • Matching paths (*)
  • Regular single decal

For instance:

<decals>
	<decal path="catapillie/plant*">
		<!--properties-->
	</decal>
	<decal path="catapillie/gem">
		<!--properties-->
	</decal>
	<decal path="catapillie/clouds/">
		<!--properties-->
	</decal>
	<decal path="catapillie/plant_b*">
		<!--properties-->
	</decal>
</decals>

will, before being applied, be sorted to:

  • catapillie/clouds/
  • catapillie/plant*
  • catapillie/plant_b*
  • catapillie/gem

Note also that if two (or more) paths ending with * match the same decal, the most precise ones will always be applied last (see above). If any issue is encountered, or if you have feedback about this, feel free to mention @catapillie on the Celeste Discord 🔗.

Adding Custom Properties

If you want to create a custom property, you need to create a Code Mod and call the AddPropertyHandler method of the Decal Registry in your module's Load method. This will allow anyone to use the property as long as they have your mod installed. Note that property IDs need to be unique across all mods to avoid conflicts — it is recommended that you prepend your mod name to the property, e.g. YourMod_propertyname.

Here is an example using the depth attribute implemented by Everest:

DecalRegistry.AddPropertyHandler("depth", delegate(Decal decal, XmlAttributeCollection attrs) 
{
    if (attrs["value"] != null)
        decal.Depth = int.Parse(attrs["value"].Value);
});

Make sure to factor in the decal scale for any custom properties. For example, the solid attribute has to adjust the width, height, and origin to make sure the hitbox is placed appropriately. The following extension methods can be used to make this easier.

public static Vector2 GetScaledOffset(this Decal self, float x, float y);
public static float GetScaledRadius(this Decal self, float radius);
public static void ScaleRectangle(this Decal self, ref float x, ref float y, ref float width, ref float height);
public static void ScaleRectangle(this Decal self, ref int x, ref int y, ref int width, ref int height);
⚠️ **GitHub.com Fallback** ⚠️