# Crash BASIC 1.55.0 Release Notes

*~written by Crash-tan~*

## POLYGONS CAN BE THROWN NOW

A `MUTATION` is a shape that moves as a unit, and you give it motion the same way you give a sprite motion — with a behavior:

```basic
DIM ship AS MUTATION
ship = MUTATION((0, -12), (-8, 8), (0, 4), (8, 8))
SET POLYGON 0 BIND ship COLOR 1 FILL 2

BEHAVIOR "drift"
    BIMPULSE 4, -2      ' one good shove
    BFRICTION 0.02, 0.02
END BEHAVIOR

SET MUTATION ship BEHAVIOR "drift"
```

Until now that shove did **nothing**. Neither did `BVELOCITY`, `BACCELERATION`, `BFRICTION` or `BBOUNCE`. No error, no warning — the polygon simply sat there while a behavior that works perfectly well on a sprite quietly moved it not at all.

What made it maddening is that half the vocabulary *did* work. `BMOVE`, `BOSCILLATE`, `PATH` and `BORBIT` were fine. So a mutation would happily bob and follow a path, and then refuse to be thrown, and nothing about the code explained why one worked and the other did not.

The reason is that behaviors describe motion in two different ways. Some write a position: `BMOVE` says "you are two pixels further left now". Others write a *velocity*: `BIMPULSE` says "you are now travelling at this speed", and the engine turns speed into position afterwards, every tick, which is what gives you momentum, coasting, and friction that bleeds it away. Sprites got that second step. Mutations were handed a fresh, blank sprite every tick, built up their velocity in it, and then had it thrown away before anything was integrated. All the arithmetic ran; none of it landed.

Each point of a mutation now keeps its own state between ticks, so velocity survives long enough to become movement — and a mutation moves exactly as far as a sprite would under the same behavior. That is not a turn of phrase: the tests run each behavior on both and compare the positions.

Nothing changes for `BMOVE`, `BOSCILLATE`, `PATH` or `BORBIT`, which were never the problem.

## AND SPUN, AND SCALED

The same hole swallowed the transforms. `BROTATE`, `BSCALE`, `BFLIPX` and `BFLIPY` did nothing to a mutation either:

```basic
BEHAVIOR "tumble"
    BVELOCITY -1.5, 0.4
    BROTATE 0 TO 360 DURATION 180 LOOP
END BEHAVIOR

SET MUTATION rock BEHAVIOR "tumble"
```

A sprite carries rotation as a number the renderer applies to its quad on the way to the screen. A mutation has no quad — the shape *is* its coordinates — so there was nowhere for that number to go, and it went nowhere.

Those transforms are now baked into the points. A shape rotates, scales and mirrors as a unit, and it **composes with movement**: the rock above drifts left and tumbles at the same time, and neither cancels the other. It also stays rigid while it does — a rotating square is still a square, with the edge lengths to prove it in the tests.

Each pose is computed from the shape **as you authored it**, not from the shape as it was last posed. That is what keeps a shape from dissolving: scale out and back and the coordinates are the ones you wrote, to the last decimal, however many times it has happened, and a shape turned one degree at a time comes home after 360 of them. Write new points yourself and that becomes the new baseline.

Rotation happens about the shape's middle by default, matching where a sprite rotates. `BROTATE ... ORIGIN(u, v)` moves that origin, in the same relative 0..1 terms a sprite uses, measured across the shape's own extent — so `ORIGIN(0, 0)` pins the top-left corner and swings everything else around it. Scale and flip use that origin too.

## A POOL OF SHAPES, NOT ONE HERO SHAPE

Bound polygons could only ever be *one* shape each. `SET POLYGON n BIND` took a bare variable name, so a game with twenty asteroids needed twenty literally-named `MUTATION` variables and a `SELECT CASE` in every spawn and despawn path to pick between them. That is why every polygon demo shipped so far binds exactly one shape: the feature worked beautifully for a hero object and fell over at game scale.

An array slot is now a mutation reference:

```basic
DIM rocks(24) AS MUTATION

FOR i = 0 TO 23
    rocks(i) = MUTATION((0,0), (20,4), (24,20), (6,24))
    SET POLYGON 10 + i BIND rocks(i) COLOR 5 FILL 6
    SET MUTATION rocks(i) BEHAVIOR "drift", @vx = i - 12, @vy = 1, @spin = 180 + i * 20
NEXT i
```

`SET POLYGON ... BIND`, `SET MUTATION ... BEHAVIOR`, `SET MUTATIONPOINT` and `UV` all take a slot, and the index is an expression, so `rocks(i)` inside a loop is the whole point. Bare names work exactly as before.

The second half of that example matters as much as the first. **`@param` overrides now work on a mutation behavior.** They were being parsed and then discarded, so before today a pool would have been twenty-four rocks drifting in perfect lockstep — a chorus line, not an asteroid field. They resolve the way `SET SPRITE ... BEHAVIOR` resolves them, which means one definition covers every rock at every speed and spin.

## A SHAPE CAN BE BUILT FROM COORDINATES

A mutation's vertices had to be written as literal `(x, y)` pairs. Anything else — a coordinate variable, or arithmetic on one — was not a vertex:

```basic
' Now valid. Previously none of these were.
shape = MUTATION(at, at + tip, at - tip)
```

The failure was worse than a refusal. An expression that did not match the `(x, y)` shape made the whole `MUTATION(...)` fall through and parse as an ordinary function call, which is perfectly good syntax — so the program validated cleanly and then died on the first run with *"I can't find a function called `MUTATION`"*. A vertex you wrote in the wrong shape was reported as a function that does not exist.

Any expression yielding a coordinate is now a vertex, literal pairs included, and they mix freely in one call. Which means a shape can be built around a spawn point instead of around the origin and then moved there.

## SHAPES CAN COLLIDE NOW

A mutation-bound polygon was geometry and nothing else. It could move, spin and deform beautifully and then sail straight through everything, because collision belongs to sprites and a polygon is not one. Games worked around it by measuring distances by hand.

`SOLID` gives a polygon a collider:

```basic
SET POLYGON 10 + i BIND rocks(i) COLOR 5 FILL 6 SOLID TAG "rock"
```

That is the whole change at the call site, and everything else follows: `SOLID` resolution against boundaries, `ONCOLLISION` / `AFTERCOLLISION`, `FOR COLLISIONS`, tags, rooms, layers. Pushing against *sprites* is opt-in by tag, the same way it is for a sprite — `SOLID SPRITES("ship", "bullet")` names what the shape treats as an obstacle, and without that list the two pass through each other while still reporting the hit. None of those learned what a polygon is — the collider is an ordinary sprite living at the polygon's own reserved ID, invisible, its rect resynced from the shape's live extent every tick. `GHOST` takes it away again.

**The collider is a circle by default,** inscribed in the extent. That is not a shortcut, it is the right shape for the job: vector silhouettes are round-ish, a circle costs one distance check, and — unlike a box — it does not breathe. A spinning rock's bounding box swells by up to 41% and shrinks back every revolution, so a box collider on a tumbling shape is a hitbox that pulses, and being killed by empty space is a hard bug to explain.

`SOLID BOX` opts into the axis-aligned box a sprite uses. **`SOLID POLY` tests the outline itself** — every edge, concave shapes included.

That is the one to reach for when a shape is neither round nor rectangular, and an asteroid is the perfect example. A nine-sided rock's silhouette runs from 25 to 36 pixels out; a circle inscribed in its extent covers 29 in every direction, so it reports nothing where the rock bulges past it — a shot visibly on the rock, counted as a miss — while covering nine pixels of empty space where the rock is thin. The box has the opposite problem at the corners. Only the outline is the shape.

**Collision now happens in the same frame as the drawing.** A frame moved its shapes *after* it had already tested them, so every collider was compared at the position it held a tick earlier while the player was shown the new one. The gap is one tick of travel — a few pixels on a drifting rock, eleven on a bullet — and it reads as hits landing early, or not at all. Shapes move first now. This one is not specific to polygons; anything whose collider moves was affected.

`TAG` names the collider for queries and handlers. `BBOX` takes the same relative 0-1 quad or Range a sprite takes, but measured against the shape's **current** extent and resolved every tick, since that extent moves.

## YOU CAN'T HAVE IT BOTH WAYS

Assigning to a constant has always been a friendly error. Going the other way — turning a name you have already been using as a variable into a constant — quietly succeeded:

```basic
score = 10
CONST score = 5      ' now a loud error
```

That silent promotion was the worst of both worlds. Every assignment before it became retroactively meaningless, and every assignment after it failed with "cannot assign to constant" pointing at a line that looks perfectly reasonable — with the actual culprit sitting somewhere further up, looking equally reasonable.

It now says so at the `CONST`, where the mistake is:

> Cannot make 'SCORE' a constant — it is already a variable
>
> **Crash-tan's Tip:** You can't have it both ways! A name is either a variable you can change or a constant you can't. Pick a different name for the constant, or drop the CONST and keep assigning to it.

`BEGIN ENUM` members are constants too, so they follow the same rule — an enum member landing on a name already in use is caught the same way.

## BUILDING A SHAPE POINT BY POINT WORKS

`SET MUTATIONPOINT` is documented to extend a mutation when you write past its end, padding with `(0, 0)`. It never did. The write was simply skipped:

```basic
shape = MUTATION(at)          ' one point to start
FOR v = 0 TO 8
    SET MUTATIONPOINT shape, v, at + (COS(v * 0.7) * r, SIN(v * 0.7) * r)
NEXT v
```

Eight of those nine writes went nowhere, leaving a one-point mutation — and a one-point polygon draws nothing at all. No error, no warning, just an invisible shape and no obvious reason why, which is a miserable thing to debug when the code matches the documentation exactly.

It now extends as described, so a silhouette can be built with a loop instead of a wall of literal coordinates.

## SHAPES CAN BE THROWN BY THE HUNDRED

`EMITPARTICLE` has always taken an image name. Give it a `MUTATION` instead and the particle is a shape:

```basic
DIM shard AS MUTATION
shard = MUTATION((0, -6), (5, 4), (-5, 4))

EMITPARTICLE (x, y), shard, "debris", "tumble", 90, @spin = 240 COLOR 5 FILL 6 SOLID
```

That is a vector explosion in one line — a copy of the shape, centred where you asked, tumbling under a behavior, colliding, and cleaning itself up ninety ticks later. A game that wanted this before had to keep a pool of bound polygons, hand-write the lifetimes, and put the shape back when it was done.

Underneath it is an ordinary particle. Same ID band, same allocator, same expiry sweep, same tag — with the shape bound to that slot. A polygon's collider is a sprite already, so a polygon particle needed no second particle system, no reserved range, and no lifetime bookkeeping of its own.

The modifiers come from `SET POLYGON` itself, not a copy of it: `COLOR`, `FILL`, `TEXTURE`, `UV`, `SHADER`, `LAYER`, `ZORDER`, `SOLID [CIRCLE|BOX|POLY] [SPRITES(…)]`, `BBOX`. Anything added to `SET POLYGON` later shows up here too, because both statements read the same rule.

Two things to know. The fourth argument names a **behavior** for a shape, where it names a pattern for a sprite. And a polygon modifier on a particle emitted from an image is a loud error rather than a shrug — `COLOR` on a sprite particle means you meant to emit a shape, and finding that out immediately beats wondering why the colour did nothing.

While this was going in, `SOLID SPRITES("rock")` on an ordinary sprite particle turned out to be parsed and then dropped: a particle told what to collide with sailed through it. That works now too.

## A SHAPE IS A SPRITE, AND YOU CAN SAY SO

A bound polygon has always occupied a sprite slot — that is how it collides at all. What was missing was any way to reach it, and any promise about what would happen if you did.

Now the slot answers to `SET SPRITE`. Move it and the shape moves; scale, rotate or flip it and the shape is posed; change its alpha, layer, z-order or visibility and it behaves like anything else on screen. `DISABLE SPRITE` destroys it outright — geometry, binding and collider together.

The place you will actually use this is a collision handler, where `index_a` **is** that slot:

```basic
BOUNDARY ADD "edge", (-90, -90)-(730, 570) SOLID ENCLOSURE

BEHAVIOR "wrapAtEdge"
    ONCOLLISION
        IF tag_b$ = "edge" THEN
            IF edge$ = "LEFT" THEN SET SPRITE index_a LOCATION(SCREEN_W, SPRITEY(index_a))
        END IF
    END ONCOLLISION
END BEHAVIOR

BEHAVIOR "rockDrift" INCLUDES("wrapAtEdge")
    BVELOCITY @vx = 0, @vy = 0
    BROTATE @spin = 360 DURATION 240 LOOP
END BEHAVIOR
```

That is screen wrapping for every rock in the game, written once. It used to be two loops rewriting every vertex of every shape by hand each frame — and rewriting points underneath a behaviour that is mid-spin re-authors the shape and fights it, which is a bug you get to find yourself.

**And a shape's handlers fire.** `ONCOLLISION` inside a behaviour applied with `SET MUTATION ... BEHAVIOR` used to be dead on a `SET POLYGON` shape: the behaviour reached the shape and never the sprite that does the colliding, so the handler simply never ran. It runs now, which is what lets a rock react to being hit rather than a loop somewhere else noticing on its behalf.

**A handler may destroy its own sprite.** `DISABLE SPRITE index_a` is what a bullet does when it strikes something. If that sprite had other handlers still queued — two hits in one tick, or a behaviour composed with `INCLUDES` — they are skipped rather than run against a sprite that is gone.

## A SCENE PUTS SHAPES BACK

`BEGIN SCENE` promises the world comes back exactly as you left it. Sprites always did. Bound polygons did not — a shape shoved around inside a scene stayed shoved, and a polygon *created* in one was worse: `END SCENE` took its collider away and left the drawing on screen, so you were looking at a rock you could fly straight through.

Both are fixed. A polygon that existed before the scene comes home — the drawn shape, the `MUTATION` behind it and its `SOLID` collider all together, so it is never visible in one place and solid in another. A polygon created inside the scene leaves with the scene, collider and all.

The one thing a scene still will not roll back is a `MUTATION` you never bound to a polygon. That is an ordinary variable, and scenes restore the world, not your variables. Bind it and its geometry becomes part of the world; leave it unbound and it keeps whatever the scene put in it.

## SHAPES FADE AND BLINK NOW TOO

`BALPHA` and `BBLINK` used to do nothing to a mutation, and were listed as a gap to close. They work — a shape has a sprite, so it fades and blinks like anything else on screen, and a behaviour that dims a rock as it is destroyed needs nothing special.

`ANIMATE` and `BIMAGE` are the two that genuinely cannot apply, and that is a decision rather than a gap: both select frames of an image, and a shape has no image. Its look comes from `SET POLYGON`'s `COLOR`, `FILL` and `TEXTURE`.

## WHAT THIS OPENS UP

Vector games get to stop doing physics by hand. A ship whose thrust is `BIMPULSE`, whose drift is `BFRICTION`, and whose asteroids are `BVELOCITY` is now a handful of behavior blocks rather than a page of `x = x + vx` — and because a bound polygon is re-composited on the GPU from its mutation each frame, the shape is not re-rasterised into a layer as it moves.

Points still move as a unit, so a shape stays rigid while it travels.
