# Crash BASIC 1.41.0 Release Notes

*~written by Crash-tan~*

## THE "GRAB A SECOND CONTROLLER" UPDATE

Controllers got a real upgrade this time. For one thing, I can finally see
*more than one of them* — so couch co-op and four-player party games are on
the table. For another, I fixed a couple of embarrassing gaps in how I
reported controllers that I'd rather not dwell on. Let's just call it a
clean slate.

And the headline: **everything here works the same on desktop, on the web,
and on phones and tablets.** Plug in a pad, or two, or four — wherever your
game runs.

---

## Up to four controllers, each on its own player number

Every controller now has a **player number**, starting at `0` for the first
one. The input functions take that number as an optional last argument, so
you can read each pad separately:

```basic
GAMEPAD PHYSICAL ON

' Player 0's left stick (the default — same as before)
x0 = JOYAXIS(0)
y0 = JOYAXIS(1)

' Player 1's left stick
x1 = JOYAXIS(0, 1)
y1 = JOYAXIS(1, 1)

' Player 2's A button
IF JOYBUTTON(0, 2) THEN PRINT "P2 jumps!"
```

Players are numbered `0` through `3` (up to four controllers). Leave the
player number off and you get player `0` — so every game you've already
written keeps reading the first controller exactly as it did.

---

## JOYCONNECTED now counts your controllers

`JOYCONNECTED()` used to be a yes/no answer. Worse — and I'll own this — it
was stuck answering "no" even when a controller was plugged in. That's
fixed, and I gave it an upgrade while I was in there.

**With no argument, `JOYCONNECTED()` now returns the *number* of controllers
connected** — `0`, `1`, `2`, `3`, or `4`. So spinning up the right number of
players is a one-liner:

```basic
players = JOYCONNECTED()
PRINT players; " controller(s) ready"

FOR p = 0 TO players - 1
  ' set up player p...
NEXT
```

And if you want to check one specific slot, **`JOYCONNECTED(p)` returns 1 if
player `p`'s controller is connected, 0 if not:**

```basic
IF JOYCONNECTED(1) THEN PRINT "Player 2 is in!"
```

Old code is safe: `IF JOYCONNECTED() THEN ...` still does the right thing,
because any non-zero count is true.

---

## JOYBUTTON, fully documented

`JOYBUTTON(button)` reads a controller button — `1` if it's held, `0` if
not — and now takes an optional player number too: `JOYBUTTON(button,
player)`. It's also finally written down in the language reference, with the
full button-number list. The same number means the same physical button on
every platform:

```
 0  A          1  B          2  X          3  Y
 4  LB         5  RB         6  LT         7  RT
 8  SELECT     9  START     10  L3        11  R3
12  D-pad Up  13  Down      14  Left      15  Right
16  Guide     17  Share
```

That's the whole standard layout now, all the way up to the **Guide/Xbox
button** and the **Share/Capture button** on the controllers that have them.

```basic
IF JOYBUTTON(0) THEN fire()       ' A
IF JOYBUTTON(9) THEN pauseGame()  ' START
```

---

## The d-pad is an axis too now

You can keep reading the d-pad as four buttons — or treat it like a stick.
`JOYAXIS(4)` is the d-pad's X (-1 left, +1 right) and `JOYAXIS(5)` is its Y
(-1 up, +1 down). One movement code path can serve stick *and* d-pad:

```basic
moveX = JOYAXIS(0)              ' left stick
IF moveX = 0 THEN moveX = JOYAXIS(4)   ' fall back to d-pad
```

---

## Triggers are analog

The shoulder triggers report their *pressure*, not just pressed-or-not.
`JOYAXIS(6)` is the left trigger and `JOYAXIS(7)` is the right, each `0` to
`1`. Great for a throttle, a brake, a charged shot:

```basic
throttle = JOYAXIS(7)          ' ease into it
speed = speed + throttle * accel
```

(They still answer as buttons 6 and 7 if you just want a yes/no.)

---

## RUMBLE — make the controller shake

New statement, and a fun one. **`RUMBLE` vibrates the controller.** Pads have
two motors — a heavy low-frequency one and a light high-frequency one — and
you can drive them together or apart:

```basic
GAMEPAD PHYSICAL ON

' Both motors, a short buzz on a hit
RUMBLE STRENGTH 0.8 DURATION 150

' Heavy engine rumble on player 2's pad
RUMBLE PLAYER 1 LOW 0.9 HIGH 0.2 DURATION 500

' Cut it short
RUMBLE STOP
```

Strength is `0` to `1`, `DURATION` is milliseconds, and it stops itself when
the time's up (or immediately on `RUMBLE STOP`). `PLAYER` is optional and
defaults to `0`. It needs `GAMEPAD PHYSICAL ON` and a controller that can
actually rumble — otherwise it's harmlessly ignored. Works on desktop, the
web, and phones/tablets.

---

## Rumble without a controller

On phones and tablets, what if there's no controller in hand — just the
on-screen gamepad, or thumbs on the glass? Now `RUMBLE` falls back to **the
device's own vibration** so your haptics still land. Plug a controller in
and the buzz goes to the controller instead; with none connected (or while
the on-screen pad is up), the phone itself shakes. Tablets with no vibration
motor sit it out gracefully.

---

## Better controller support on macOS

While I was in the controller code, I taught the Mac version to talk to
controllers through Apple's own controller system. Two nice results:

- **Bluetooth controllers work now**, not just USB ones — Xbox, PlayStation,
  and MFi pads connect wirelessly and Just Work.
- **Rumble works on the Mac too** (it quietly didn't before).

(On the web, Bluetooth controller support depends on your browser — Safari
sees them; some others only see USB.)

---

## FIXED and FIXED$ — tidy numbers

Sticks are noisy — read one and you'll see the value jittering out in the
far decimal places. Two new functions clean that up:

- **`FIXED(value, places)`** rounds a number to `places` decimals and hands
  back a *number* you can keep doing math with.
- **`FIXED$(value, places)`** formats it to exactly that many decimals as
  *text* — a clean, steady readout with no floating-point fuzz.

```basic
PRINT FIXED$(JOYAXIS(0), 2)     ' "-0.57", not "-0.5681342..."
aim = FIXED(JOYAXIS(0), 2)      ' settle the jitter before you use it
```

---

## BOUNDARY ADD FROM — collision you drew in the editor

Draw your walls, floors, ramps, and trigger zones visually in the IDE's map
editor, export them, and load the whole set in a single line:

```basic
BOUNDARY ADD FROM("level1.boundaries.json")
```

Every region comes in exactly as if you'd hand-written a `BOUNDARY ADD` for
each one — tags, solids, enclosures, slopes, and per-layer scoping all
carried over. No more transcribing rectangles by hand. (And if the file's
missing or malformed, it tells you loudly instead of quietly loading half a
level.)

---

## Text-heavy games run smooth on phones

If your game prints a lot of fast-changing text — a live score, a timer,
debug readouts, anything redrawing every frame — Android could bog down hard,
right to a crawl. That's fixed. On-screen text now draws a whole line in one
stroke instead of a letter at a time, and the slowdown is simply gone. Same
pixels on screen, a *lot* less work behind them.

---

## Compatibility

This is an add-on, not a rewrite. Every existing controller call keeps
working unchanged:

- `JOYAXIS(axis)` and `JOYBUTTON(button)` still read the first controller.
- `JOYCONNECTED()` is still true when a controller is present — it just
  tells you *how many* now instead of a plain yes.
- The new player number is always optional and 0-based, matching the rest
  of the language.

Asking for a player that isn't there is harmless: `JOYCONNECTED` gives `0`,
`JOYAXIS` gives `0.0`, and `JOYBUTTON` gives `0`.
