API Reference
FRED-80 carts run on standard Lua 5.4 — all Lua standard libraries (math, string, table, coroutine) are available. The functions below are FRED-80 built-ins.
A6000 tip: Prefer the built-in sin/cos/atan2, clamp/mid/flr over the math library — they use lookup tables and are significantly faster on the 68080.
System
time()
Returns elapsed seconds since the cart started.
time() → number
-- bob a sprite up and down
local x = 320 + sin(time() * 0.5) * 100
stat(n)
Returns engine diagnostic values.
stat(n) → number
| n | Returns |
|---|---|
| 0 | Current FPS |
print(stat(0) .. "fps", 580, 469, 3)
Math
Angles use the 0..1 full-circle convention — 0 = 0°, 0.25 = 90°, 0.5 = 180°.
sin(t)
Lookup-table sine. Fast on A6000 — integer table, no floating-point cost.
sin(t) → number
| Param | Description |
|---|---|
| t | Angle in 0..1 full-circle range |
local y = 240 + sin(time() * 0.3) * 80
cos(t)
Lookup-table cosine. Fast on A6000 — integer table, no floating-point cost.
cos(t) → number
| Param | Description |
|---|---|
| t | Angle in 0..1 full-circle range |
local x = 320 + cos(time() * 0.3) * 80
atan2(y, x)
Returns the angle of vector (x, y) in 0..1 range. Use to aim things toward a point.
atan2(y, x) → number
-- aim from center toward mouse
local angle = atan2(my - 240, mx - 320)
local tx = 320 + cos(angle) * 40
local ty = 240 + sin(angle) * 40
line(320, 240, tx, ty, 7)
rnd([n])
Returns a random float in [0, n). Defaults to 1.0.
rnd([n]) → number
local x = rnd(640) -- random x position
local slot = flr(rnd(8)) -- random int 0..7
flr(x)
Floor — rounds down to the nearest integer.
flr(x) → integer
local tile_x = flr(x / 16)
ceil(x)
Ceiling — rounds up to the nearest integer.
ceil(x) → integer
local rows = ceil(count / 8)
abs(x)
Returns the absolute value of x.
abs(x) → number
if abs(dx) < 4 then snap() end
min(a, b) / max(a, b)
Returns the smaller or larger of two values.
min(a, b) → number
max(a, b) → number
max(a, b) → number
x = max(0, x) -- clamp to left edge
x = min(x, 624) -- clamp to right edge
mid(a, b, c)
Returns the median of three values. Handy for clamping —
mid(lo, val, hi).mid(a, b, c) → number
x = mid(0, x, 624) -- keep x within screen
clamp(v, lo, hi)
Clamps v between lo and hi.
clamp(v, lo, hi) → number
health = clamp(health + 10, 0, 100)
Input
btn(name)
Returns
true while the named button is held down.btn(name) → boolean
| name | Description |
|---|---|
| "left" "right" "up" "down" | D-pad directions |
| "z" "x" "c" | Action buttons |
| "enter" "escape" | Menu buttons |
if btn("right") then x = x + 2 end
btnp(name)
Returns
true only on the first frame the button is pressed. Use for single-trigger actions like jumping or shooting.btnp(name) → boolean
if btnp("z") then jump() end
joy_axis(joy, axis)
Returns the raw axis value from a joystick.
joy_axis(joy, axis) → integer (-32768..32767)
| Param | Description |
|---|---|
| joy | Joystick index 0–3 |
| axis | Axis index (0=X, 1=Y typically) |
local ax = joy_axis(0, 0) / 32768 -- normalise to -1..1
x = x + ax * 3
joy_btn(joy, btn)
Returns
true if a joystick button is pressed.joy_btn(joy, btn) → boolean
if joy_btn(0, 0) then fire() end
joy_hat(joy, hat)
Returns the raw hat switch bitmask.
joy_hat(joy, hat) → integer
| Bit | Direction |
|---|---|
| 1 | Up |
| 2 | Right |
| 4 | Down |
| 8 | Left |
local h = joy_hat(0, 0)
if h & 1 ~= 0 then move_up() end
Drawing
cls([c])
Clears the screen to a palette colour.
cls([c])
| Param | Description |
|---|---|
| c | Palette index 0–255. Default 0 (transparent/black) |
cls() -- clear to black
cls(3) -- clear to colour 3
camera([x, y])
Sets the global scroll offset. All draw calls shift by (-x, -y). Call with no arguments to reset to (0, 0).
camera([x, y])
camera(cam_x, cam_y) -- scroll world
tilemap_draw(tm, 0, 0)
camera() -- reset for HUD
print("100 pts", 4, 4, 7)
zoom([n])
Sets pixel scale 1–8. All coordinates and sizes are multiplied by n. Default 1.
zoom([n])
zoom(2)
spr(0, 160, 120) -- draws at 32x32 pixels
zoom(1)
pset(x, y, c)
Draws a single pixel. Use
pset_batch_xy when drawing many pixels per frame.pset(x, y, c)
| Param | Description |
|---|---|
| x, y | Screen position in pixels |
| c | Palette index 0–255 |
pset(320, 240, 7) -- white pixel at center
pget(x, y)
Returns the palette index of the pixel at (x, y). Returns 0 if out of bounds.
pget(x, y) → integer
local c = pget(mx, my) -- sample colour under mouse
pal(i, r, g, b)
Remaps palette entry i to a 24-bit RGB colour at runtime. Takes effect immediately. Indices 0 (transparent) and 240 (collision mask) are protected.
pal(i, r, g, b)
| Param | Description |
|---|---|
| i | Palette index 1–255 (0 and 240 are protected) |
| r, g, b | Red, green, blue 0–255 |
-- flash player red on hit, then restore
pal(7, 255, 0, 0)
spr(player_slot, px, py)
pal(7, 240, 240, 240)
pset_batch(data)
Draws N pixels in a single Lua→C call. Much faster than calling pset() in a loop.
pset_batch(data)
| Param | Description |
|---|---|
| data | Flat table: {x1,y1,c1, x2,y2,c2, ...} |
pset_batch({10,20,7, 11,20,7, 12,20,3})
pset_batch_xy(c, data)
Draws N pixels all in the same colour. Faster than pset_batch when colour is uniform (e.g. starfields).
pset_batch_xy(c, data)
| Param | Description |
|---|---|
| c | Palette index for all pixels |
| data | Flat table: {x1,y1, x2,y2, ...} |
local stars = {}
for i = 1, 80 do
stars[i*2-1] = rnd(640)
stars[i*2] = rnd(480)
end
function _draw()
pset_batch_xy(15, stars) -- 80 stars, one C call
end
line(x0, y0, x1, y1 [, c])
Draws a Bresenham line between two points.
line(x0, y0, x1, y1 [, c])
| Param | Description |
|---|---|
| x0, y0 | Start point |
| x1, y1 | End point |
| c | Palette index. Default 7 |
line(0, 0, 639, 479, 7)
rect(x0, y0, x1, y1 [, c])
Draws a rectangle outline. Avoid on the hot path on A6000 — pre-bake static UI into a canvas instead.
rect(x0, y0, x1, y1 [, c])
rect(10, 10, 100, 50, 7)
rectfill(x0, y0, x1, y1 [, c])
Draws a filled rectangle. Avoid on the hot path on A6000 — pre-bake static UI into a canvas instead.
rectfill(x0, y0, x1, y1 [, c])
rectfill(0, 460, 639, 479, 1) -- HUD bar
circ(cx, cy, r [, c])
Draws a circle outline.
circ(cx, cy, r [, c])
circ(320, 240, 50, 7)
circfill(cx, cy, r [, c])
Draws a filled circle.
circfill(cx, cy, r [, c])
circfill(320, 240, 20, 8) -- filled red circle
print(str, x, y [, c])
Draws text using the built-in 8×8 pixel font.
\n advances to the next line.print(str, x, y [, c])
| Param | Description |
|---|---|
| str | String to draw |
| x, y | Top-left position |
| c | Palette index. Default 7 |
print("SCORE: " .. score, 8, 8, 7)
print("GAME OVER\npress Z", 260, 200, 8)
fb_fade(amount [, floor])
Subtracts
amount from every palette index in the framebuffer each call, creating a decay/trail effect without cls(). AMMX-accelerated on A6000. floor clamps the minimum palette index so pixels never decay into index 0 (transparent pink) — default 1. Pass floor=0 only if you have remapped palette entry 0 to a dark colour.fb_fade(amount [, floor])
| Param | Description |
|---|---|
| amount | Palette index to subtract per frame (1–255). Default 1. |
| floor | Minimum index after decay. Default 1. |
function _draw()
fb_fade(3) -- decay all pixels, skip cls()
circfill(ox, oy, 6, 91) -- bright dot leaves colour trail
end
Sprites
The sprite bank holds 128 slots (0–127), each 16×16 pixels. Painted in the SPRITE tab, saved as
sprites.spr. Colour 0 is always transparent.spr(n, x, y [, flip_h, flip_v])
Draws a sprite from the bank at (x, y). Colour 0 is transparent.
spr(n, x, y [, flip_h, flip_v])
| Param | Description |
|---|---|
| n | Sprite slot 0–127 |
| x, y | Screen position (top-left of sprite) |
| flip_h | Mirror horizontally. Default false |
| flip_v | Mirror vertically. Default false |
spr(0, player.x, player.y)
spr(2, enemy.x, enemy.y, true, false) -- flipped
spr_batch(n, pos)
Draws slot n at N positions in a single Lua→C call. AMMX-accelerated on A6000 at zoom=1. Use instead of a spr() loop.
spr_batch(n, pos)
| Param | Description |
|---|---|
| n | Sprite slot 0–127 |
| pos | Flat table: {x1,y1, x2,y2, ...} |
local bullet_pos = {}
function _draw()
-- fill bullet_pos with live positions...
spr_batch(BULLET_SLOT, bullet_pos)
end
spr_batch_composite(tiles, pos)
Draws a multi-tile composite sprite (up to 16 tiles) at N positions in one call. Use for large sprites made from multiple bank slots.
spr_batch_composite(tiles, pos)
| Param | Description |
|---|---|
| tiles | Flat table: {slot,dx,dy, slot,dx,dy, ...} up to 16 tiles |
| pos | Flat table: {x1,y1, x2,y2, ...} |
-- 2x2 tank from 4 bank slots (32x32 total)
local TANK = {0,0,0, 1,16,0, 2,0,16, 3,16,16}
spr_batch_composite(TANK, tank_positions)
spr_flag(n [, mode])
Sets a persistent blend mode for slot n. Applied every time spr(n,...) is called. Pass no mode to reset to normal.
spr_flag(n [, mode])
| mode | Description |
|---|---|
| "stipple" | Checkerboard transparency |
| "blend50" | 50% mix with background |
| "blend25" | 25% mix with background |
spr_flag(GHOST_SLOT, "blend50") -- always semi-transparent
spr_flag(GHOST_SLOT) -- reset to normal
spr_alpha(n, x, y, mode)
One-off draw with an explicit blend mode, without changing the slot's persistent flag.
spr_alpha(n, x, y, mode)
spr_alpha(SHIELD_SLOT, sx, sy, "blend50")
spr_new(w, h, pixels)
Creates a custom Sprite object from a flat pixel table (palette indices, row by row). Colour 0 is transparent. Automatically compiled as opaque if no transparent pixels, enabling faster blitting.
spr_new(w, h, pixels) → Sprite
| Param | Description |
|---|---|
| w, h | Width and height in pixels |
| pixels | Flat table of palette indices, 1-based, row by row |
local px = {}
for i = 1, 16*16 do px[i] = 0 end
px[8*16+8+1] = 7 -- white dot at (8,8)
local dot = spr_new(16, 16, px)
spr_blit(spr, x, y [, flip_h, flip_v])
Draws a custom Sprite object created with spr_new().
spr_blit(spr, x, y [, flip_h, flip_v])
spr_blit(dot, 320, 240)
spr_get(n, px, py)
Returns the palette index of pixel (px, py) in sprite slot n.
spr_get(n, px, py) → integer
local c = spr_get(0, 8, 8)
hget(n, px, py)
Returns 1 if the hitbox layer is set at (px, py) of slot n, else 0.
hget(n, px, py) → 0 or 1
if hget(PLAYER, 8, 8) == 1 then -- center is collidable
end
spr_is_opaque(n)
Returns true if slot n has no transparent pixels. Use to branch between fast opaque blitting and transparency handling.
spr_is_opaque(n) → boolean
if spr_is_opaque(slot) then
spr_batch(slot, positions)
end
spr_debug(n, x, y)
Draws a debug overlay showing transparent pixels (pink), hitbox pixels (green), and pixels that are both (Gunnar's Green). Use during development to verify hitbox coverage.
spr_debug(n, x, y)
spr_debug(PLAYER, player.x, player.y)
spr_hit(n1, x1, y1, n2, x2, y2)
Pixel-perfect collision test between two sprites. Returns true if their hitbox layers overlap. Paint hitboxes in the SPRITE tab using the M toggle.
spr_hit(n1, x1, y1, n2, x2, y2) → boolean
if spr_hit(PLAYER, px, py, ENEMY, ex, ey) then
take_damage()
end
Canvas
Canvases are offscreen pixel buffers. Pre-bake anything static into a canvas in
_init() and draw it with a single canvas_draw() per frame.canvas_new(w, h)
Creates an offscreen canvas w×h pixels. All pixels start at 0 (transparent). Maximum 640×480.
canvas_new(w, h) → Canvas
local bg = canvas_new(640, 480)
canvas_set([c])
Redirects all drawing into canvas c. Call with no argument to restore drawing to the main screen.
canvas_set([c])
canvas_set(bg)
rectfill(0, 0, 639, 479, 2)
canvas_set() -- back to screen
canvas_draw(c, x, y [, scale, flip_h, flip_v, alpha])
Composites a canvas onto the current draw target. Fastest at scale=1, no flip, alpha=100 (opaque) — uses a pre-clipped row-copy path.
canvas_draw(c, x, y [, scale, flip_h, flip_v, alpha])
| Param | Description |
|---|---|
| c | Canvas object |
| x, y | Top-left position on screen |
| scale | Integer ≥ 1. Default 1 |
| flip_h, flip_v | Mirror flags. Default false |
| alpha | Opacity 0–100. Default 100. Values below 100 are slow on A6000 |
canvas_draw(bg, 0, 0) -- fast full-screen blit
canvas_draw(portrait, 10, 10, 3) -- 3x scaled portrait
image_load(path)
Loads a .img file from disk and returns it as a Canvas. Convert PNGs first with
tools/img_import.py.image_load(path) → Canvas
local bg = image_load(CART_DIR .. "background.img")
Tilemaps
8 map slots (0–7), each 64×32 tiles. Tiles are 16×16 pixels. Paint maps in the MAP tab; they save to
map0.dat–map7.dat.mload(slot)
Loads map<slot>.dat from the cart folder, builds a pre-rendered Tilemap using the current sprite bank, and returns the Tilemap object.
mload(slot) → Tilemap
function _init()
tm = mload(0)
map_use(tm)
end
tilemap_new(tw, th, cols, rows, tiles, data)
Creates a Tilemap from scratch without a file.
tilemap_new(tw, th, cols, rows, tiles, data) → Tilemap
| Param | Description |
|---|---|
| tw, th | Tile width and height in pixels |
| cols, rows | Map dimensions in tiles |
| tiles | Array of Sprite objects (1-based) |
| data | Flat cols×rows array of tile indices (0 = empty) |
tilemap_draw(tm, x, y)
Draws tilemap tm with its top-left at (x, y). Fast scanline copy path at zoom=1.
tilemap_draw(tm, x, y)
tilemap_draw(tm, 0, 0)
tilemap_parallax(tm, x, y)
Composites tilemap tm treating colour 0 as transparent. Use for layered parallax scrolling.
tilemap_parallax(tm, x, y)
-- two-layer parallax scroll
camera(flr(bg_x), 0)
tilemap_parallax(bg_tm, 0, 0)
camera(flr(fg_x), 0)
tilemap_draw(fg_tm, 0, 0)
camera()
tilemap_set(tm, col, row, idx)
Sets the tile at 1-based (col, row) and updates the pre-rendered buffer immediately.
tilemap_set(tm, col, row, idx)
tilemap_set(tm, 3, 2, 5) -- place tile 5 at column 3, row 2
tilemap_get(tm, col, row)
Returns the tile index at 1-based (col, row).
tilemap_get(tm, col, row) → integer
local t = tilemap_get(tm, 3, 2)
map_use(tm)
Registers tm as the active map for mget/mset.
map_use(tm)
map_use(tm)
mget(x, y)
Returns the tile index at 0-based tile (x, y) of the active map. Returns 0 if out of bounds.
mget(x, y) → integer
local tile = mget(flr(px/16), flr(py/16))
mset(x, y, idx)
Sets the tile at 0-based (x, y) of the active map. Updates immediately.
mset(x, y, idx)
mset(5, 3, 0) -- clear tile at (5,3)
Sound
sfx(freq [, dur, wave, vol, channel, env])
Plays a synthesised sound effect.
sfx(freq [, dur, wave, vol, channel, env])
| Param | Description |
|---|---|
| freq | Hz number, or note string e.g. "A4" |
| dur | Duration in seconds. Default 0.5 |
| wave | 0=Square, 1=Sine, 2=Triangle, 3=Sawtooth, 4=Noise |
| vol | Volume 0–100. Default 100 |
| channel | 0–7. Default -1 (first free channel) |
| env | Optional table with ADSR, LFO, filter keys (see below) |
| env key | Range | Description |
|---|---|---|
| pw | 0.0–1.0 | Pulse width / duty cycle (square only) |
| a | ms | Attack time |
| d | ms | Decay time |
| s | 0–100 | Sustain level |
| r | ms | Release time |
| lfo | Hz | LFO rate (0 = off) |
| lfd | 0.0–1.0 | LFO depth |
| lft | 1/2/3 | LFO target: 1=pulse width, 2=volume, 3=both |
| flt | 0/1/2 | Filter: 0=off, 1=low-pass, 2=high-pass |
| fc | Hz | Filter cutoff |
| fq | 0.1–10 | Filter resonance (Q) |
sfx(note("C4"), 0.2, 0, 80) -- short blip
sfx(110, 1.5, 0, 90, 0, {a=10, d=200, s=60, r=300}) -- ADSR pad
sfx(220, 1.5, 0, 90, 0, {flt=1, fc=600, fq=3.0}) -- low-pass
note(name)
Returns the frequency in Hz for a note name. Middle C is "C4". Supports sharps and flats.
note(name) → number
sfx(note("A4"), 0.3, 0, 80) -- 440 Hz
sfx(note("C#4"), 0.3, 1, 80)
sfx(note("Gb5"), 0.3, 2, 80)
music(seq [, loop, channel])
Plays a note sequence. Each entry is {freq, dur, [wave], [vol], [env keys...]}. A frequency of 0 is a rest.
music(seq [, loop, channel])
local melody = {
{note("C4"), 0.2, 0, 70},
{note("E4"), 0.2, 0, 70},
{note("G4"), 0.4, 0, 80},
{0, 0.1}, -- rest
}
music(melody, true) -- loop forever
music_stop([channel])
Stops music on the specified channel. Default stops all channels.
music_stop([channel])
music_stop() -- stop all music
music_stop(2) -- stop channel 2 only
audio_stop([channel])
Stops all sound (sfx and music) on the specified channel. Default stops all channels.
audio_stop([channel])
audio_stop() -- silence everything
snd_load(slot, path)
Loads a pre-baked .pcm file into a sound slot. PCM files are baked from Tommy compositions using
bake aud in the shell.snd_load(slot, path)
| Param | Description |
|---|---|
| slot | Sound slot index |
| path | Path to .pcm file |
snd_load(0, "music.pcm")
snd_play(slot, loop, channel, vol)
Plays a loaded PCM sound. Zero CPU cost on A6000 — uses SAGA DMA hardware playback.
snd_play(slot, loop, channel, vol)
| Param | Description |
|---|---|
| slot | Sound slot loaded with snd_load() |
| loop | true to loop continuously |
| channel | Audio channel 0–3 |
| vol | Volume 0–100 |
function _init()
snd_load(0, "music.pcm")
snd_play(0, true, 0, 80) -- loop on channel 0
end
snd_stop([channel])
Stops PCM playback. Pass a channel number (0–3) to stop that channel only. Omit to stop all channels.
snd_stop([channel])
| Param | Description |
|---|---|
| channel | Channel 0–3. Omit to stop all channels. |
snd_stop(0) -- stop channel 0 only
snd_stop() -- stop all channels
Tweens
Up to 16 simultaneous tweens. Always call
tween_free() when done to release the slot.tween_new(dur, start, end [, easing])
Creates a tween and returns its slot ID.
tween_new(dur, start, end [, easing]) → id
| Param | Description |
|---|---|
| dur | Duration in seconds |
| start, end | Start and end values |
| easing | "linear", "inQuad", "outQuad", "inOutQuad", "inCubic", "outCubic", "inSine", "outSine", "inOutSine", "outBounce", "inBounce", "outElastic", "inElastic", "outBack", "inBack" |
local slide = tween_new(0.4, 660, 480, "outBack")
tween_step(id)
Advances the tween by one frame. Returns the current value and a done flag.
tween_step(id) → value, done
local x, done = tween_step(slide)
canvas_draw(panel, flr(x), 100)
if done then tween_free(slide) end
tween_reset(id)
Rewinds the tween to its start value without freeing the slot.
tween_reset(id)
tween_reset(slide)
tween_free(id)
Releases the tween slot.
tween_free(id)
tween_free(slide)
Bezier Curves
Pure polynomial math — no trig, safe on the 68080. Use
bez_render() for anything called every frame.bezier(t, x0,y0, x1,y1, x2,y2 [, x3,y3])
Returns x, y along a quadratic (3 points) or cubic (4 points) bezier curve at position t.
bezier(t, ...) → x, y
| Param | Description |
|---|---|
| t | Position along curve 0..1 |
| x0,y0 … x2,y2 | Control points for quadratic curve |
| x3,y3 | Optional 4th point for cubic curve |
bx, by = bezier(t, 100,240, 200,50, 300,240)
bez_render(n, x0,y0, x1,y1, x2,y2 [, x3,y3])
Pre-bakes n evenly-spaced points along the curve into a flat integer table {x1,y1, x2,y2, ...}. Do this once in _init() to avoid per-frame float math on A6000.
bez_render(n, ...) → table
local PATH
function _init()
PATH = bez_render(32, 20,240, 160,40, 300,240)
end
function _update()
local i = flr(enemy.t * 31) * 2
enemy.x = PATH[i + 1]
enemy.y = PATH[i + 2]
end
Physics
Verlet-integration rigid bodies and spring constraints. Bodies accumulate forces each frame; call
body_update() once per frame to integrate. Always free bodies and springs when done.body_new(x, y [, r, mass])
Creates a point-mass body at position (x, y). Returns a handle.
body_new(x, y [, r, mass]) → handle
| Param | Description |
|---|---|
| x, y | Initial position |
| r | Collision radius. Default 8. |
| mass | Mass. Default 1.0. |
local ball = body_new(320, 100, 8, 1.0)
body_force(handle, fx, fy)
Adds a force vector to the body for this frame. Accumulates — call multiple times for multiple forces (gravity, wind, player input).
body_force(handle, fx, fy)
body_force(ball, 0, 0.5) -- gravity
body_update(handle [, damp])
Integrates velocity, applies damping, resets accumulated forces. Call once per frame per body.
body_update(handle [, damp])
| Param | Description |
|---|---|
| damp | Velocity multiplier per frame. Default 0.98. |
body_force(ball, 0, 0.5)
body_update(ball, 0.99)
body_pos(handle)
Returns the body's current position.
body_pos(handle) → x, y
local bx, by = body_pos(ball)
circfill(bx, by, 8, 9)
body_vel(handle)
Returns the body's current velocity.
body_vel(handle) → vx, vy
local vx, vy = body_vel(ball)
body_circle_collide(handle, ox, oy, or [, restitution])
Tests and resolves collision between this body and a static circle at (ox, oy) with radius or. Call after
body_update().body_circle_collide(handle, ox, oy, or [, restitution])
| Param | Description |
|---|---|
| ox, oy, or | Static circle centre and radius |
| restitution | Bounce factor 0–1. Default 0.5. |
body_circle_collide(ball, 320, 400, 60, 0.7)
body_aabb_collide(handle, x0, y0, x1, y1 [, restitution])
Resolves collision between this body and a static axis-aligned rectangle. Use for walls, floors, platforms.
body_aabb_collide(handle, x0, y0, x1, y1 [, restitution])
body_aabb_collide(ball, 0, 0, 639, 479, 0.6) -- screen boundary
body_free(handle)
Releases the body slot. Always call when a body is no longer needed.
body_free(handle)
body_free(ball)
spring_new(a, b, rest_len, stiffness)
Creates a spring constraint between two body handles. Returns a handle.
spring_new(a, b, rest_len, stiffness) → handle
| Param | Description |
|---|---|
| a, b | Body handles to connect |
| rest_len | Natural length of the spring in pixels |
| stiffness | Spring constant. 0.1 = loose, 1.0 = rigid. |
local s = spring_new(head, tail, 30, 0.4)
spring_update(handle [, iters])
Applies spring forces to both connected bodies. Call after
body_update() for each connected body. More iterations = stiffer behaviour at the cost of CPU.spring_update(handle [, iters])
| Param | Description |
|---|---|
| iters | Constraint solver iterations. Default 4. |
spring_update(s, 6)
spring_draw(handle [, col])
Draws a line between the two connected bodies. Useful for debugging.
spring_draw(handle [, col])
| Param | Description |
|---|---|
| col | Palette index. Default 7. |
spring_draw(s, 11)
spring_free(handle)
Releases the spring slot.
spring_free(handle)
spring_free(s)
Persistent Storage
64 number slots shared across all carts, backed by
fred80.dat in PROGDIR (the FRED-80 install folder on Amiga, the working directory on macOS). Survives cart restarts.dget(slot)
Reads a number from persistent storage slot 0–63. Returns 0.0 if never written.
dget(slot) → number
local hi = dget(0) -- load high score
dset(slot, val)
Writes a number to persistent storage slot 0–63 and saves to disk immediately.
dset(slot, val)
if score > hi_score then
hi_score = score
dset(0, hi_score)
end