mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-03 20:59:44 +00:00
feat: render transport-ship trail cosmetic as a gradient (#4454)
## What
Renders the `transportShipTrail` cosmetic effect in-game. Transport
ships already left a trail, but it was always drawn in the player's
**territory color** — this wires the selected effect through to the
renderer so the trail shows the player's chosen **gradient**.
## How
- **Per-player effect texture** (`RGBA32F`, mirrors the palette texture)
keyed by `smallID`, sampled by the trail fragment shader. Each row holds
a gradient color; spare alpha channels carry the color count,
`colorSize`, and `movementSpeed`.
- **Shader**
([trail.frag.glsl](src/client/render/gl/shaders/map-overlay/trail.frag.glsl))
cycles a flowing gradient through the color list: 1 color → flat, 2+ →
animated bands scrolling along the trail. No effect (count 0) falls back
to the territory color; alt-view keeps affiliation colors.
- **WebGLFrameBuilder** resolves each player's catalog attributes (the
in-game cosmetic is only `{ name, effectType }`; the style/colors live
in the catalog) and encodes them. Resolution is decoupled from the
first-seen palette path so it retries until the catalog loads, and
unparseable colors are dropped so bad catalog data degrades to the
territory color rather than rendering black.
## Schema
Collapses the trail attributes to a single gradient shape:
```ts
{ type: "gradient", colors: string[], colorSize: number, movementSpeed: number }
```
- `colors` — solid = one color, rainbow = the spectrum, gradient = two
or more.
- `colorSize` — band width (tiles per color band; `1` is the default, ~4
tiles).
- `movementSpeed` — scroll rate along the trail (tiles/sec; `0` =
static).
## Notes
- Animation is render-only (local time), no simulation/determinism
impact.
- The catalog (`cosmetics.json`, served by the closed-source API) must
ship effects in this `{ type: "gradient", colors, colorSize,
movementSpeed }` shape.
- Band thickness (`4.0` base in the shader) and the gradient frequency
are visual constants picked without in-game verification — easy to tune.
## Testing
- `tsc --noEmit`, ESLint, Prettier, `build-prod` all clean.
- Schema + Privilege test suites updated for the gradient shape (92
tests pass).
- Not yet visually verified in a running game (effect selection is
flare-gated).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,48 +1,27 @@
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TransportShipTrailAttributes } from "../../core/CosmeticSchemas";
|
||||
|
||||
// A flowing spectrum used for the "rainbow" transport-ship-trail preview.
|
||||
const RAINBOW_GRADIENT =
|
||||
"linear-gradient(90deg,#ff0000,#ff8a00,#ffe600,#28c76f,#00a8ff,#7d5fff,#ff0000)";
|
||||
|
||||
// Neutral fallback for attribute types we don't recognize.
|
||||
const UNKNOWN_BG = "#444";
|
||||
// Neutral fallback when a trail has no usable colors.
|
||||
const EMPTY_BG = "#444";
|
||||
|
||||
/**
|
||||
* Render a swatch preview of a transport-ship-trail's attributes, filling its
|
||||
* container: solid = flat color, pulse = same color pulsing, rainbow = full
|
||||
* spectrum, gradient = two-color blend. Unknown attribute types render a neutral
|
||||
* swatch (we ignore types we don't know about).
|
||||
* container. A trail is a list of colors: one color renders as a flat swatch,
|
||||
* two or more as a left-to-right gradient (a multi-color list reads as a
|
||||
* rainbow). An empty list renders a neutral swatch.
|
||||
*/
|
||||
export function renderTransportShipTrailSwatch(
|
||||
attributes: TransportShipTrailAttributes,
|
||||
): TemplateResult {
|
||||
switch (attributes.type) {
|
||||
case "rainbow":
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md"
|
||||
style="background:${RAINBOW_GRADIENT};"
|
||||
></div>`;
|
||||
case "gradient":
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md"
|
||||
style="background:linear-gradient(90deg,${attributes.color},${attributes.color2});"
|
||||
></div>`;
|
||||
case "pulse":
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md animate-pulse"
|
||||
style="background:${attributes.color};"
|
||||
></div>`;
|
||||
case "solid":
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md"
|
||||
style="background:${attributes.color};"
|
||||
></div>`;
|
||||
default:
|
||||
// Unknown / unrecognized style — neutral swatch.
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md"
|
||||
style="background:${UNKNOWN_BG};"
|
||||
></div>`;
|
||||
}
|
||||
const colors = attributes.colors;
|
||||
const background =
|
||||
colors.length === 0
|
||||
? EMPTY_BG
|
||||
: colors.length === 1
|
||||
? colors[0]
|
||||
: `linear-gradient(90deg,${colors.join(",")})`;
|
||||
return html`<div
|
||||
class="w-full h-full rounded-md"
|
||||
style="background:${background};"
|
||||
></div>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user