Image¶
A sprite. It can be a static picture, an animation or an item's icon, and it can have text drawn on top of it: a sign, a plaque, a labelled diagram.
{
"Type": "Image",
"TexturePath": "Data/PeacefulEnd.Campgrounds/Campgrounds/Textures/StarterTent",
"TextureSourceRectangle": { "X": 0, "Y": 5, "Width": 48, "Height": 59 },
"Scale": 2,
"Alignment": "Center"
}
An image is sized by its sprite: TextureSourceRectangle × Scale. If that's wider than the space available it's scaled down to fit, with a warning. Text never widens it.
Image fields¶
| Property | Type | Default | Description |
|---|---|---|---|
ItemId optional |
string |
— | A qualified item ID such as (O)24, whose icon is drawn. When set, TexturePath and TextureSourceRectangle are ignored. The item's name and description also fill in DisplayName and Description automatically, so ItemId alone gives you the sprite and a vanilla-style tooltip. |
Frames optional |
list of frames |
— | Animation frames. When omitted, the sprite is static. |
HoverFrames optional |
list of frames |
— | Animation frames played while the cursor is over the element, replacing Frames for as long as it stays there. See Hover frames. |
FrameDuration optional |
number |
100 |
How long a frame is shown when it doesn't specify its own Duration, in milliseconds. |
TextArea optional |
Rectangle |
the whole sprite | Where text is drawn, in unscaled sprite pixels relative to TextureSourceRectangle's top-left, not to the texture. This is how you place a label inside a sign's recessed panel. The text block is centred vertically within this area. |
TextScale optional |
number |
1 |
The text's scale, independent of Scale, which sizes the sprite. |
TextAlignment optional |
Left | Center | Right |
Center |
How each line of text is aligned within TextArea. Distinct from Alignment, which places the whole image on the page. |
Rotation optional |
number |
0 |
How much rotation is applied to the texture. Note: does not affect text! |
Origin optional |
Vector2 |
{ X: 0.0, Y: 0.0 } |
The pivot point the sprite rotates and scales around, in unscaled sprite pixels relative to TextureSourceRectangle's top-left. It changes what the sprite turns and grows about, never where it rests, so a still sprite at its own Scale looks identical at any value. Note: does not affect text! |
Frames¶
Each entry in Frames:
| Property | Type | Default | Description |
|---|---|---|---|
SourcePoint required |
Point |
— | The coordinate of the sprite for this frame. Automatically inherits the element's TextureSourceRectangle for height and width. |
Duration optional |
number |
the element's FrameDuration |
How long this frame is shown in milliseconds. |
Scale optional |
number |
1 |
A multiplier on the element's Scale while this frame draws. See Frame scale. |
Condition optional |
string |
— | A game state query deciding whether this frame plays. When omitted the frame always plays. |
Frames loop, and the cycle runs off game time, so two identical animations on a page play in lockstep.
A frame whose Condition fails is skipped, not paused on. The cycle gets shorter and the remaining frames close the gap, the same way a hidden element lets the ones below it close up. Conditions are re-checked while the book is open, so an animation can gain and lose frames as the game state changes.
When every frame's condition fails, the element falls back to drawing TextureSourceRectangle on its own. An animation that's entirely conditional therefore goes still rather than disappearing.
TextureSourceRectangle is required when animating
It's the measuring stick: it defines the element's size, while Frames defines what's drawn. Without it, the whole sprite sheet becomes the element.
Point it at a frame you'd be happy to see
Because it's the fallback, TextureSourceRectangle should be a sprite that stands on its own. Aim it at a blank cell and a fully conditional animation renders as nothing.
{
"Type": "Image",
"TexturePath": "LooseSprites/GemBird",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 32, "Height": 32 },
"Scale": 3,
"Alignment": "Center",
"Frames": [
{ "Duration": 1000, "SourcePoint": { "X": 0, "Y": 0 } },
{ "Duration": 100, "SourcePoint": { "X": 32, "Y": 0 } },
{ "Duration": 100, "SourcePoint": { "X": 64, "Y": 0 } }
]
}
A candle that only flickers after dark. Both frames drop out during the day, leaving the unlit sprite the source rectangle points at:
{
"Type": "Image",
"TexturePath": "{{ModId}}/candle",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 16, "Height": 16 },
"Scale": 4,
"Frames": [
{ "SourcePoint": { "X": 16, "Y": 0 }, "Condition": "TIME 1800 2600" },
{ "SourcePoint": { "X": 32, "Y": 0 }, "Condition": "TIME 1800 2600" }
]
}
Frame scale¶
Scale on a frame is the one thing that changes a sprite's size mid-animation. The element is measured once, at TextureSourceRectangle × the element's own Scale, and that measurement is what reserves space on the page and what the cursor is tested against. A frame at 1.2 draws twenty percent larger over the top of that reserved space rather than pushing the elements below it down.
It grows from Origin, which defaults to the sprite's top-left corner, so a scaled frame spreads right and down unless you move the pivot. Put Origin in the middle of the source rectangle (8, 8 for a 16×16 sprite) and the frame grows evenly in every direction instead. The pivot itself doesn't move as the frame scales, so a pulse stays put rather than creeping across the page.
A pulse needs no extra art at all, just the same cell drawn bigger for a moment:
{
"Type": "Image",
"TexturePath": "{{ModId}}/pulse",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 16, "Height": 16 },
"Scale": 4,
"Origin": { "X": 8, "Y": 8 },
"SpacingAfter": 24,
"Frames": [
{ "Duration": 700, "SourcePoint": { "X": 0, "Y": 0 } },
{ "Duration": 120, "SourcePoint": { "X": 0, "Y": 0 }, "Scale": 1.15 },
{ "Duration": 200, "SourcePoint": { "X": 0, "Y": 0 } }
]
}
Hover frames¶
HoverFrames is a second frame list that takes over while the cursor is on the element. It's the animated counterpart to HoverTextureSourceRectangle, which swaps a single still.
Both lists are sized by TextureSourceRectangle, so this changes what's drawn and never the element's layout. Everything a frame understands works in either list: Duration, Condition and Scale all behave the same, and both lists share the element's FrameDuration as their default.
{
"Type": "Image",
"TexturePath": "{{ModId}}/lantern",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 16, "Height": 16 },
"Scale": 4,
"DisplayName": "Lantern",
"Frames": [
{ "Duration": 600, "SourcePoint": { "X": 0, "Y": 0 } },
{ "Duration": 600, "SourcePoint": { "X": 16, "Y": 0 } }
],
"HoverFrames": [
{ "Duration": 120, "SourcePoint": { "X": 0, "Y": 16 } },
{ "Duration": 120, "SourcePoint": { "X": 16, "Y": 16 } }
]
}
Leaving HoverFrames out means the normal animation simply keeps playing under the cursor, which is the behaviour every existing book already has.
An empty hover animation falls back rather than freezing. If every frame in HoverFrames is conditioned out, the element carries on with Frames instead of dropping to a still. The order of preference is HoverFrames, then Frames, then TextureSourceRectangle, so a hover animation can come and go with the game state without interrupting the idle loop.
HoverFrames alone makes an element hoverable. In a page's Background or Foreground, an element with nothing else to offer is transparent to the cursor. A hover animation counts as something to offer, the same way a HoverTextureSourceRectangle does, so it will be reachable without needing a tooltip or an action.
Hover frames don't restart on hover
Frame cycles run off absolute game time so that identical animations stay in lockstep, and HoverFrames is no exception. The hover animation joins at whatever point in its cycle the clock is at, rather than starting from its first frame. That's invisible for a loop, such as a flicker or a jiggle, and noticeable for a one-shot reveal. Write hover animations as loops.
Text fields¶
Optional: leave Text out for a plain picture.
| Property | Type | Default | Description |
|---|---|---|---|
Text optional |
string |
— | The text to draw. Wraps automatically to the available width, including breaking words that are too long for a line on their own. \n forces a line break. |
FontType optional |
font type |
varies | Which font to draw with. The default differs per element type. |
TextColor optional |
color |
the book's default | The text colour. |
Sprite fields¶
| Property | Type | Default | Description |
|---|---|---|---|
TexturePath optional |
string |
— | The asset name of the sprite sheet, such as Assets/PeacefulEnd.Parchment/panelFrame2 or a vanilla path like LooseSprites/Cursors. This is a game asset name, not a file path in your content pack. Load your PNG into the game's content with a Content Patcher Load patch first. See Loading your art. |
TextureSourceRectangle optional |
Rectangle |
the whole texture | The area of the sprite sheet to draw. Almost always needed: without it, the whole sheet is used, which for a multi-sprite sheet means a much larger element than you expect. |
HoverTextureSourceRectangle optional |
Rectangle? |
— | An alternate source rectangle drawn while the cursor is over the element. Must be the same size as TextureSourceRectangle. The element's layout is measured from the normal rectangle, so a differently-sized hover sprite will overhang its own bounds. For a moving hover state on an Image, use HoverFrames instead. |
TintColor optional |
color |
white | A colour multiplied into the sprite. Multiplying means it can only darken: red on grey art gives red, red on blue art gives near-black. Best on neutral or greyscale art. |
SpriteEffects optional |
None | FlipHorizontally | FlipVertically |
None |
Mirrors the sprite within its own bounds. It does not move the element. If flipping appears to shift the art sideways, the sprite has uneven transparent padding inside its source rectangle. Combine with a comma: "FlipHorizontally, FlipVertically". Ignored by Panel and Button, which are nine-sliced. |
Common fields¶
Scale on an Image is the sprite scale. Use TextScale for the text.
| Property | Type | Default | Description |
|---|---|---|---|
Type required |
element type |
— | Which kind of element this is. Determines every other field below. |
Id optional |
string |
— | An optional identifier for this element. Not used for navigation, purely for your own reference. |
Alignment optional |
Left | Center | Right |
Left |
Where the element sits within its container's width. Only has an effect when the element is narrower than the space available (see Layout). |
Scale optional |
number |
1 |
The element's scale. Its meaning depends on the element type: sprite scale for Image, Panel, Banner, Button and Divider. Font scale for Title, Heading and Paragraph. See Units and scale. |
SpacingAfter optional |
int |
8 |
The gap between this element and the next one, in unscaled sprite pixels × Scale. Not applied after the last visible element, so a trailing gap can't appear at the bottom of a page or panel. |
MarginLeft optional |
int |
0 |
Space reserved to the element's left, in unscaled sprite pixels × Scale. This narrows the width the element measures against, so text wraps at the indented width rather than overflowing. |
MarginRight optional |
int |
0 |
Space reserved to the element's right, in unscaled sprite pixels × Scale. |
Position optional |
Point |
{ X: 0, Y: 0 } |
The element's position, in screen pixels. Only used in Background, Foreground, Underlay and Overlay, where elements are placed rather than stacked. Unlike every other spacing field this is not multiplied by Scale. Changing an element's scale resizes it in place rather than moving it. |
Condition optional |
string |
— | A game state query. When it evaluates false the element is hidden, and elements below it close the gap. Re-checked several times a second while the book is open. |
Action optional |
string |
— | A trigger action run when the element is clicked. When set, the element becomes interactive: any element type can have one, not just Button. |
Actions optional |
list of string |
— | Trigger actions run in order when the element is clicked. Combined with Action rather than replacing it, so an element with both runs Action first. |
Sound optional |
string |
bigSelect |
The cue played when the element is clicked. Only used when Action or Actions is set, and played once however many actions run. Set to null for a silent click. |
HoverAction optional |
string |
— | A trigger action run when the cursor moves onto the element. Runs on entry, not while the cursor rests there. Sound doesn't apply. |
HoverActions optional |
list of string |
— | Trigger actions run in order when the cursor moves onto the element. Combined with HoverAction rather than replacing it, so an element with both runs HoverAction first. |
DisplayName optional |
string |
— | The bold title of the element's hover tooltip. Works in every list, including a page's Background and Foreground. |
Description optional |
string |
— | The body of the element's hover tooltip. Works in every list, including a page's Background and Foreground, where setting it is also what makes an otherwise decorative element reachable by the cursor. |
Watch for transparent padding
An image's size is its source rectangle, not the pixels painted in it. If your sprite has empty rows at the top or bottom of its rectangle, the element reserves space for them and the picture looks oddly offset. Tighten the rectangle to the art. See Preparing your art.