effects

package
v0.0.28 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package effects is Linefire's particle system: one pool that drives every cosmetic spray in the game — explosions, sparks, exhaust, debris — and the presets that describe them.

It is deliberately free of any game state. A pool is handed its own randomness and is drawn through an explicit camera and scale, so the same explosion plays in the game, in the editors' previews, and in Linefire Skirmish, rather than each of them growing its own.

Everything here is LIGHT: particles are drawn additively, and a particle fades by dropping its additive gain, never by lowering its color's alpha. Additive blending ignores what lies under it, so a lower alpha alone would leave a dying spark exactly as bright as a fresh one.

Index

Constants

View Source
const (
	// DefaultMax caps a pool: a hard backstop so a pile-up of explosions can never
	// grow the slice without bound. Particles are purely cosmetic, so emits past
	// the cap are dropped silently and the oldest keep aging out.
	DefaultMax = 1500
)
View Source
const DefaultMoteSize = 1.6

DefaultMoteSize is the radius of a portal mote in world units. It is exported so a caller asking for a different one can say what it is relative to.

Variables

View Source
var (
	SparkColor    = color.RGBA{0xff, 0xc8, 0x60, 0xff} // warm explosion core
	DebrisColor   = color.RGBA{0xff, 0x90, 0x40, 0xff} // hotter ember chunks
	ThrusterColor = color.RGBA{0x90, 0xe0, 0xff, 0xff} // pale blue engine exhaust
)

Colors shared by the presets below.

View Source
var (
	ExplosionStreaks = Burst{
		N: 14, Col: SparkColor, Style: StyleStreak,
		SpeedMin: 2.0, SpeedMax: 6.0, LifeMin: 18, LifeMax: 34, Drag: 0.90,
	}
	ExplosionChunks = Burst{
		N: 6, Col: DebrisColor, Style: StyleDot,
		SpeedMin: 1.0, SpeedMax: 3.5, LifeMin: 26, LifeMax: 46, Drag: 0.93, Size: 1.6,
	}
)

ExplosionStreaks and ExplosionChunks together are a ship dying: warm streaks flung out fast, plus a few slower glowing chunks that linger. Play both with Pool.Explosion.

Functions

func DrawPortal added in v0.0.7

func DrawPortal(dst *ebiten.Image, p Portal)

DrawPortal renders a portal as a tunnel: a thin outer ring, and motes born ON that ring travelling STRAIGHT to the centre, where they die. The radial convergence — no spiral, no inner rings — is what reads as looking down a tunnel. Each mote takes a fresh random spoke every cycle, so the stream scatters instead of tracing a few thick arms.

Nothing is stored: the whole vortex is a function of Seconds, so a caller can draw one wherever it likes without owning any state.

func DrawPulseRing added in v0.0.16

func DrawPulseRing(dst *ebiten.Image, x, y, radius float64, col color.RGBA, seconds, scale, dpr float64)

DrawPulseRing draws a breathing ring around something worth looking at — a power-up on the ground, a portal standing in the world. radius is what it encircles, in screen pixels; scale converts the swell and the inset from world units, and dpr the stroke.

It is here rather than in either game because it is the mark Linefire uses to say "this is worth flying to", and that has to look the same wherever it says it.

func SpokeAngle added in v0.0.7

func SpokeAngle(i, c int) float64

SpokeAngle is a stable pseudo-random spoke for mote i on cycle c: constant while the mote falls inward, fresh when it respawns, so the stream never repeats a fixed arm.

Types

type Burst

type Burst struct {
	N                  int
	Col                color.RGBA
	Style              Style
	SpeedMin, SpeedMax float64
	LifeMin, LifeMax   int
	Drag               float64
	Size               float64 // dot radius (StyleDot only)
}

Burst is a named emitter preset: a radial spray of particles sharing a look and a speed/lifetime range. A new effect should be a new preset, not new code.

type Particle

type Particle struct {
	X, Y    float64
	PX, PY  float64 // previous point, for the streak style
	VX, VY  float64
	Drag    float64
	Life    int
	MaxLife int
	Size    float64 // dot radius in world units (StyleDot only)
	Col     color.RGBA
	Style   Style
}

Particle is a short-lived element of an effect. It carries its own motion (velocity plus drag) and look (style, size, color) so one pool drives everything. Emit fills PX/PY; the rest is the caller's.

type Pool

type Pool struct {
	// contains filtered or unexported fields
}

Pool holds the live particles.

func New

func New(max int, random func() float64) *Pool

New returns an empty pool holding at most max particles and drawing its randomness from random, which must return values in [0, 1).

The caller supplies the source so the effects inherit whatever determinism it has: the game uses its global one, a seeded simulation its own.

func (*Pool) Burst

func (p *Pool) Burst(x, y float64, b Burst)

Burst sprays one preset radially from (x, y).

func (*Pool) Draw

func (p *Pool) Draw(dst *ebiten.Image, v View)

Draw renders the pool, fading each particle with its remaining life.

func (*Pool) Emit

func (p *Pool) Emit(pt Particle)

Emit appends one particle, respecting the pool cap.

func (*Pool) Explosion

func (p *Pool) Explosion(x, y float64)

Explosion plays a death: streaks plus glowing chunks.

func (*Pool) Len

func (p *Pool) Len() int

Len reports how many particles are alive.

func (*Pool) Particles

func (p *Pool) Particles() []Particle

Particles exposes the live pool for inspection. The slice is the pool's own: read it, do not keep it past the next Step.

func (*Pool) Step

func (p *Pool) Step()

Step advances and ages every particle, dropping the dead ones. Pure motion, no rendering, so it is cheap and testable on its own.

func (*Pool) Thruster added in v0.0.7

func (p *Pool) Thruster(x, y, fx, fy float64)

Thruster trails exhaust from (x, y) — the ship's rear — for one frame of acceleration along the forward unit vector (fx, fy). Call it once per thrusting frame; the counts are small because of that, and the pool cap is the backstop.

The plume is a NARROW fan of streaks thrown fast out the tail. Fat discs were the wrong shape entirely: they smear a blob over the hull instead of a jet.

type Portal added in v0.0.7

type Portal struct {
	X, Y   float64 // centre, screen pixels
	Radius float64 // rim, screen pixels
	Col    color.RGBA

	// Seconds is the animation clock. It only has to advance smoothly: the motes
	// are a function of it, so nothing is stored between frames.
	Seconds float64

	// Fade scales the whole thing out, 1 for a portal at full strength. A
	// permanent portal leaves it at 1; a transient one — a ship arriving — rides
	// it down as the vortex closes.
	Fade float64

	// MoteSize is the radius of one mote in world units. Zero uses DefaultMoteSize.
	MoteSize float64

	// Speed is how fast a mote travels rim -> centre, in cycles per second. Zero
	// uses the package default. A portal that stands in the world can let them
	// drift; a brief one has to pull them all the way in before it closes, or the
	// convergence never arrives anywhere.
	Speed float64

	// HideRing drops the rim the motes are born on, leaving only the convergence.
	// A portal that stands in the world wants the ring — it is a place you can fly
	// into. One that exists for a moment to deliver a ship does not: without the
	// rim the motes read as something materialising rather than a hole opening.
	HideRing bool

	DPR   float64 // device pixels per logical px, for the ring stroke
	Scale float64 // screen pixels per world unit, for the mote size
}

Portal is one frame of a vortex, in screen space.

type Style

type Style uint8

Style selects how a particle is drawn.

const (
	StyleStreak Style = iota // fading line from the previous to the current point
	StyleDot                 // fading disc of Size world units (chunks, motes)
)

type View

type View struct {
	Cam ebiten.GeoM // world -> screen

	// DPR is device pixels per logical pixel, applied to stroke widths so a
	// streak is the same apparent thickness on any display.
	DPR float64

	// PixelScale is device pixels per world unit, applied to dot radii so a chunk
	// is the same size in the world however the camera is zoomed.
	PixelScale float64

	// Glow widens strokes and discs for the emissive pass that feeds the bloom.
	Glow bool
}

View is how a pool is placed on screen: the camera transform plus the two scales the drawing needs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL