cycle_palette
Rotates chosen palette indices across generated frames to animate color cycling in indexed Aseprite sprites, making pixels appear to flow without moving them.
Instructions
Animate a sprite by cycling a run of palette colours, the oldest trick in the medium: the pixels do not move, the colours do, and water flows.
**What this ships, and why.** Aseprite's file format carries a palette per frame, but
its Lua API does not expose one, so the real thing cannot be authored from here.
Measured on 1.3.18.6: `#spr.palettes` is 1 and stays 1, the collection is read-only
(`spr.palettes[2] = ...` and `table.insert` both raise "attempt to index a nil value
(field '__setters')"), `Palette` has no `frame` property, `Sprite:newPalette` does not
exist, and `Sprite:setPalette` replaces the single sprite-wide palette no matter which
frame `app.frame` is on, as does `app.command.LoadPalette`. So this is the fallback:
the frames are generated, it costs one frame of storage each, and it works everywhere
a frame does, including in a GIF or a PNG sequence.
What rotates is the pixels' own indices, not the palette. On an indexed sprite those
are the same picture, and the index move is exact: no colour matching, nothing routed
through `nearest_index`, and the palette comes back byte for byte as it went in, so
`get_palette` still shows the ramp that was authored.
Args:
indices: The palette indices to rotate, **in the order the colours travel**. At
least two, distinct, each one in the palette, and none of them an entry that
cannot draw (the sprite's transparent index, or an entry whose alpha is 0):
rotating one of those through the cycle would make drawn pixels vanish.
`list_palette_usage` reports the contiguous runs worth passing here.
frame_count: How many frames the cycle occupies. Defaults to `len(indices)`, which
is one frame per colour, and is capped there: past that the rotation repeats a
frame already written, so the extra frames cost storage and show nothing new.
step: How far the colours travel per frame. Positive moves them forward along
`indices`; negative moves them back. A multiple of `len(indices)` is refused
rather than silently producing identical frames.
layer: Cycle only this layer's pixels. Default: every pixel layer.
The sprite must have exactly one frame. A cycle generates the whole timeline from the
one drawn frame, so a sprite that already animates would have its timeline redefined,
and that is refused rather than guessed at: `duplicate_frame` the pose into a sprite
of its own first.
Refuses, before anything is written, an index outside the palette (it names the size),
an index that cannot draw, a repeated index, a step that is a whole number of laps, and
a cycle none of whose indices appear in the art, which would write identical frames and
animate nothing. That last refusal names the indices the art *is* drawn with.
Returns the frames it wrote and how many pixels moved on each, `closes` (whether the
rotation returns to where it started at the wrap), and `warnings` when the cycle does
not close, or when an index in it has no pixels to travel through. `warnings` is
absent rather than empty when there is nothing to say.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| step | No | Default: 1. | |
| layer | No | ||
| indices | Yes | ||
| filename | Yes | ||
| frame_count | No |