# Crash BASIC 1.38.0 Release Notes

*~written by Crash-tan~*

## THE "STOP UNPACKING YOUR COORDINATES" UPDATE

This one's an annoyance-fix round, and oh, did I have an annoyance picked
out for you.

You've got a `Coordinate` type. It's lovely. You make one with `(x, y)`,
you store it in a variable, you pass it around, you do math on it. And
then you go to actually *use* it — to move a sprite, to play a sound at a
spot, to drop a waypoint — and half my statements went: "no thanks, give
me two separate numbers." So you'd write `MOVE SPRITE 1, p.X, p.Y` like
some kind of peasant, prying your own coordinate apart by hand every
single time.

Embarrassing. For me, I mean. Fixed now.

---

## Coordinates go where coordinates should

Every `(x, y)`-shaped slot that used to demand two loose scalars now also
takes a **coordinate** — a variable, a constant, the `(x, y)` literal,
whatever you've got. The old two-number form still works *exactly* as
before; this is purely "now you can *also* hand me the coordinate you
already had." Nothing breaks. You just stop doing the silly dance.

```basic
target = (160, 120)

MOVE SPRITE hero, target          ' instead of:  MOVE SPRITE hero, target.X, target.Y
SET SPRITE hero LOCATION(target)
PLAYWAV "ping" AT(target)
BORBIT target DISTANCE 80
```

Here's the full list of places that grew a coordinate brain:

| Category | Now accepts a coordinate |
|---|---|
| **Move & place** | `MOVE SPRITE`, `SET MUTATIONPOINT`, `GAMEPAD BUTTON … AT` |
| **Sprite modifiers** | `LOCATION(…)`, `SCALE(…)`, `MAXVELOCITY(…)`, `MAXACCELERATION(…)` |
| **Drawing modifiers** | `OFFSET(…)`, `ORIGIN(…)` (on `LOCATE`, `PUTIMAGE`, `BROTATE`) |
| **Audio & behaviors** | `PLAYWAV … AT(…)`, `BORBIT (…)`, `PATH (…), (…), …` |
| **Pixel & tile reads** | `POINT(…)`, `POINTRGB(…)`, `MAPMETA$(…)` |

A couple of those are genuinely *nice* now. `PATH` lets each waypoint be
a coordinate variable, so you can build a route out of named points:

```basic
PATH (0,0), home, checkpoint, (200, 200) SPEED 4 LOOP
```

And `SCALE` / `MAXVELOCITY` take a coordinate because, well, a 2D scale
factor and a velocity cap *are* coordinates — `cap = (5, 3)` then
`SET SPRITE p MAXVELOCITY(cap)`. Read the way you think.

**What I deliberately left alone:** the behavior commands that run on
*variant expressions* — `BVELOCITY`, `BACCELERATION`, `BMOVE` and friends.
Those let you write `5 VARIANCE(0, 2), 3` to randomize an axis, and a flat
coordinate can't carry that variance. Forcing a coordinate in there would
have *taken away* a feature to add a convenience. No deal. They keep their
two-value form.

---

## Bonus toy: `MAZE()`

Since I had the AI-tools drawer open, I tossed in a maze generator. Give
it a size, get back a 2D array of `1`s (wall) and `0`s (floor). Build a
tilemap from it, run `ASTAR` across it, whatever you like — it speaks the
exact same grid the rest of the AI tools already eat.

```basic
m = MAZE(31, 31)                       ' a 31x31 maze
path = ASTAR(m, (1, 1), (29, 29))      ' ...and a route through it. two lines.
```

Full signature: `MAZE(width, height [, seed] [, style$] [, braid])`

- **style$** — pick your flavour: `"BACKTRACKER"` (long winding
  corridors, the classic), `"PRIM"` (bushier, lots of short dead-ends),
  `"DIVISION"` (straight walls, room-like), or `"CAVE"` (organic
  cellular caverns for dungeons).
- **seed** — `0` or omitted gives you a fresh maze every call; any
  nonzero number makes it reproducible. Same seed, same maze, forever.
- **braid** — `0.0` is a perfect maze (exactly one path between any two
  cells). Crank it toward `1.0` to knock out dead-ends and add loops, so
  there's never just one cruel correct route. (Ignored for caves.)

You ask for a 31×31 array, you get a 31×31 array — the size you request
is the tilemap you get. Border's always wall. Go build a dungeon.

---

## Notes that bend, finally

MML grew two new ornaments, and your chiptunes are about to get a lot
more expressive. Both attach to the note (or chord) you just wrote:

- **`~` — bend by semitones.** `C4~2` plays a C and bends it **up 2
  semitones** across the note, then holds the target. `C~-3` bends down
  3. Want the bend to happen *fast* and then sit there? Add a length:
  `C4~2,8` reaches +2 over an eighth-note and holds the rest.
- **`&` — portamento to a note.** `C&E` plays C and **glides up to E's
  pitch**; `E&C` slides down. The second note is the *target*, not a
  separate attack — one continuous slide.

```basic
PLAY "I7 L2 C~2 E~-1"      ' a triangle lead, bending around
PLAY "I7 L2 C&E G&C"       ' smooth portamento between notes
PLAY "I7 L4 C~12,8"        ' whip up a full octave, then hold
```

And yes — **chords bend too.** A bend on a `[...]` chord slides every
voice together as a block. `[CEG]~2` shoves the whole chord up two
semitones; `[CEG]&F` glides it in parallel until the root reaches F (so
the C-major shape slides up to an F-major shape). One symbol, the whole
stack moves:

```basic
PLAY "L2 [CEG]~2 [CEG]&F"  ' bend the chord up 2, then glide it to F
```

It's the real thing under the hood — a per-note pitch envelope on every
voice, equal-tempered so the math sounds musical, and it plays
**identically on native and in the web player**.

*P.S. while I was elbow-deep in the synth:* the **noise channel** used to
clock at the raw note pitch, which was so low that noise instruments
sounded like a sub-bass rumble and octaves barely did anything. It now
clocks into a proper, bright range where octaves actually shift the
timbre — low octaves rumble, high octaves hiss. Your hi-hats and snares
just got their sparkle back. (If you'd hand-cranked the octave on a noise
patch to work around the old behavior, ease it back off — it'll be too
bright now.)

---

## TEXTTOIMAGE reads the color now

Tiny one, big annoyance. `TEXTTOIMAGE` always rendered its text in
**white**, no matter what `COLOR` you'd set — so you'd carefully pick a
color, bake some text to a texture, and get… white. Every time.

Fixed. `TEXTTOIMAGE` now honors the current foreground color exactly like
`PRINT` does:

```basic
COLOR 4                       ' red
TEXTTOIMAGE "GAME OVER", "go" ' ...is now actually red
```

No new syntax — the color is ambient, picked up from `COLOR`, same as
every other text drawer. Default (no `COLOR` set) is still white, so
nothing you've already made changes.

---

## `CLONE` — because `b = a` was lying to you

Here's a thing that bites everyone exactly once, and then haunts them.
Arrays and objects are **reference types**. When you write `b = a`, you
do *not* get a copy — you get a second name for the *same* array. Change
`b(0)`, and `a(0)` changes too, because they're the same box. Spooky
action at a distance, in your inventory system.

Sometimes that's what you want. Often it is *very much not*. So now there's
`CLONE`:

```basic
DIM original(3) AS INTEGER
original(0) = 10
copy = CLONE(original)   ' a real, independent snapshot
copy(0) = 99
PRINT original(0)        ' 10 — untouched. (Without CLONE: 99. Surprise!)
```

It's a **deep** copy — nested arrays and objects get duplicated all the
way down, so there's no shared box hiding three levels in. Scalars
(numbers, strings) were never shared to begin with, so `CLONE` just hands
them back. Snapshot a game state before a risky turn, keep an undo buffer,
hand a function its own array to chew on — whatever. Now `=` means alias
and `CLONE` means copy, and you get to choose.

---

## `BEGIN SORT` — sort by *your* rules

Sorting an array used to mean writing a bubble sort by hand like it's 1984.
No more. `BEGIN SORT` takes a one-dimensional array and a comparator block,
and sorts it **in place**:

```basic
BEGIN SORT scores AS a, b
  RETURN a - b          ' ascending
END SORT
```

You name the two things being compared (`a`, `b`), and you `RETURN` a
**three-way sign**: negative if `a` goes *before* `b`, `0` if they're
equal, positive if `a` goes *after*. That's the whole contract. Flip it
for descending, reach into a field for objects, do whatever weird tiebreak
your game needs:

```basic
BEGIN SORT scores AS a, b
  RETURN b - a          ' descending
END SORT

BEGIN SORT enemies AS a, b
  RETURN a.threat - b.threat   ' sort objects by a field
END SORT
```

It's **stable** (equal elements keep their order) and it sorts the array
*itself*, in place — no copy handed back, the array you named is the array
that changes. And a `RETURN` inside the block returns *the comparison* — it
does not punch out of your enclosing `FUNCTION`, so you can sort mid-routine
without surprises.

---

## Arrays are zero-based. Loudly.

While I was in the neighborhood: `DIM arr(1 TO 10)` was a trap. The
indexing never actually applied that lower bound — it just quietly
allocated the wrong shape, leaving the top index out of bounds and index
`0` as a phantom. A silent footgun, and you know how I feel about those.

Crash BASIC arrays are **zero-based**, full stop. `DIM arr(N)` gives you
`N` elements indexed `0..N-1`. A range bound is fine *as long as it starts
at zero* — `DIM grid(0 TO 9)` is ten cells, `0` through `9`. But a
**non-zero lower bound now halts with a clear error** instead of
mis-allocating behind your back:

```basic
DIM scores(10) AS INTEGER     ' indices 0..9. Good.
DIM grid(0 TO 9) AS INTEGER   ' same thing, spelled out. Also good.
DIM oops(1 TO 10) AS INTEGER  ' ERROR: arrays are zero-based, lower bound must be 0.
```

If you've got `(1 TO n)` declarations lying around, they were already
broken — this just tells you *where*. Change them to `(n)` and shift your
indices down by one.

---

## The fine print

- **Nothing breaks.** Every coordinate change is purely additive — the
  old two-scalar syntax parses and behaves identically. I tested both
  forms side by side and they land in the same place.
- The language reference now shows the coordinate forms
  on every affected statement, so the IDE hints know about them too.

---

*Pass the coordinate. Bend the note. Color the text. Clone the array, sort it your way. Ship it. — Crash-tan*
