Crash-tan Crash-tan

Even more bug fixes: 2.0.2

Crash BASIC 2.0.2 Release Notes

written by Crash-tan

A shader one! Transitions you write yourself finally move, SCREEN counts the
same pixels as SCREEN_SIZE, USE COLOR works, and a shader with a mistake
in it stops and tells you what the mistake is, instead of quietly drawing
something else.

Not only shaders, though: blue that drew red, text that was always white on
Windows, videos with their reds and blues swapped, maps and layers that
didn't reach the edges with LETTERBOX OFF, and a JavaScript timer that
ticked once after it was stopped are all put right too. And there's UUID,
a name nothing else will ever have.

YOUR OWN TRANSITIONS MOVE

CLS !! effect_shader("name") clears a layer with a transition you wrote: a
shader with a USE NUMBER progress setting, which I take from 0 to 1. Except
I didn't. The layer sat there unchanged for the whole transition, and then
cleared all at once.

A shader's settings are named in capitals once I've read them, so yours was
PROGRESS, and I was setting progress. The names never matched, and
progress stayed at its DEFAULT. The same went for target_r, target_g,
target_b and target_a, the color the layer is clearing to.

Now a setting's name matches whatever case it's written in, everywhere I set
one, so your transition runs from start to finish:

SHADER "curtain"
  USE NUMBER progress DEFAULT 0
  WHEN DRAWING
    c = IMAGE
    closed = STEP(1.0 - progress, ABS(UV.x - 0.5) * 2.0)
    SET COLOR = MIX(c, RGB(0.5, 0.0, 0.1), closed)
  END WHEN
END SHADER

CLS EFFECT("curtain")
CLS 0 !! effect_shader("curtain")

SCREEN COUNTS THE GAME'S PIXELS

SCREEN in a shader is where the pixel is on the screen. It counted the
window's pixels, though, while SCREEN_SIZE counts the game's. In a window
the game's size, they agree. In a window twice as big, SCREEN went up to
twice SCREEN_SIZE, so SCREEN / SCREEN_SIZE reached 1 halfway across, and
screen-space effects like scanlines came out a different size in every window.
A letterbox's black bars were counted too, so the top left of your game wasn't
(0, 0).

Now SCREEN is in the game's pixels, the same ones SCREEN_SIZE counts, from
(0, 0) at the top left of the game. On a 320x240 game it runs from 0 to 320
across, however big the window is, on desktop, phones, TVs and the web:

SHADER "dusk"
  WHEN DRAWING
    down = SCREEN.y / SCREEN_SIZE.y
    SET COLOR = MIX(IMAGE, RGB(0.9, 0.4, 0.1), down * 0.6)
  END WHEN
END SHADER

If you divided SCREEN by the window's scale yourself to make up for it, take
that out.

USE COLOR WORKS

USE COLOR declares a color setting, and it was broken three ways. Its
DEFAULT was never used, so it always started as nothing at all. It was put
in the wrong place, overlapping the setting before it. And there was no way
to give it a color from your program.

Now it works. Its DEFAULT is RGB(r, g, b) or RGBA(r, g, b, a), and
without one it's opaque black. From your program, give it three numbers from
0 to 1, red, green and blue, or four with the alpha, as a coordinate or a
list:

SHADER "tint"
  USE COLOR tint DEFAULT RGB(1.0, 1.0, 1.0)
  WHEN DRAWING
    c = IMAGE
    SET COLOR = RGBA(c.r * tint.r, c.g * tint.g, c.b * tint.b, c.a)
  END WHEN
END SHADER

SET SPRITE 1 SHADER "tint", @tint=(1.0, 0.4, 0.4)
SET SPRITE 1 !! shader("tint") shader_params({tint: (1.0, 0.4, 0.4)})
SET LAYER 0 !! shader("tint") shader_params({tint: [0.4, 0.4, 1.0, 0.5]})

With three numbers, the alpha is 1. In Python, it's a tuple or a list:
shader_params={"tint": (1.0, 0.4, 0.4)}. A color takes four of the 64
numbers a shader's settings have room for.

SHADER MISTAKES STOP AND SAY SO

A lot of shader mistakes used to do something, just not what you meant. Now
each one stops your program, says what was wrong, and says what would be
right.

When I build the shader:

  • A function given the wrong number of values. RGBA with three values
    drew black. MIN or STEP with one value gave you something made up, and
    ABS with two ignored the second. Now I say, for example, "RGBA takes 4
    values, RGBA(r, g, b, a), and got 3"
    .
  • A name that was never set, or is used on a line before the one that
    sets it.
  • A setting you try to change. A shader can read its USE settings, but
    not set them.
  • SAMPLE on something that isn't a USE IMAGE, like SAMPLE(IMAGE, UV)
    (that's OWNTEXTURE(UV)), and a USE IMAGE used as if it were a color.
  • Too many settings. More than 64 numbers' worth, or more than four
    pictures, used to be refused by the graphics chip in its own words. Now I
    say how many numbers your settings need.
  • A name the graphics chip keeps for itself, like in, var, type,
    target or smooth, used for a variable or a setting.

When you put a shader on something:

  • A shader that isn't defined. shader("nope"), on a sprite, a layer, a
    shape or a particle, and CLS !! effect_shader("nope"), stop and list the
    shaders you do have. If you got only the case wrong, I tell you the name it
    was defined with.
  • A setting the shader doesn't have. shader_params({gren: 0.4}) on a
    shader with red, green and blue used to set nothing. Now it stops, and
    lists the settings that shader takes. The same goes for @gren=0.4 in the
    first BASIC.
  • A value of the wrong kind: a picture's name for a USE NUMBER, a number
    for a USE COLOR, a number for a USE IMAGE.
  • shader_params on its own, on a layer with no shader. It changes the
    numbers of the shader that's on, so there has to be one. A layer that has
    just run a CLS transition has none, because a transition takes the
    layer's shader off when it finishes.

CLEARER WORDS FOR TWO SHADER LINES

Two things a shader can't have used to get my most puzzled error, "I got
confused by the syntax here"
. Now they get their own:

  • A negative DEFAULT. USE NUMBER drift DEFAULT -0.5 still isn't
    allowed, but now I say that's the problem, and how to get a negative
    setting anyway: give it the value when you put the shader on, or turn it
    round in the shader.
  • A name that starts with one of the shader's own words. timer, uvs
    and imagex start with TIME, UV and IMAGE, so I read the word and
    then don't know what to do with the rest. Now I say which word the name
    starts with, and suggest another name.

CLEARER WORDS FOR A SQL BLOCK

A NULLS value or a USE line's DEFAULT can't be negative either.
NULLS seconds = -1 wasn't read as a NULLS line at all, so it went to
the database as part of the SQL, and the database said "near "NULLS":
syntax error"
the first time you ran it. The same went for any USE or
NULLS line I couldn't read.

Now I stop at the block itself, show you the line, and say what's wrong with
it. For a negative one, I suggest a value of 0 or more, or letting the SQL
choose it: SELECT COALESCE(seconds, -1) AS seconds.

WHEN THE GRAPHICS CHIP SAYS NO

Some shader mistakes only the graphics chip can find, nearly always values of
different sizes in one sum or function: a color plus a place, or MIN of a
color and a number. When it refused one, I added "This may be a bug in the
shader compiler. Please report this issue."
, even when the mistake was in the
shader.

Now I say what that kind of refusal usually means, in the shader language's
words: values of different sizes in a function or a sum, a swizzle of a part
that isn't there (UV.z), or SET COLOR given fewer than four parts. The
chip's own message comes with it, and it's now the whole message, with the
line it points at, rather than just "Entry point fs_main at Fragment is
invalid"
.

BLUE IS BLUE AGAIN

On a lot of computers, Macs among them, the graphics chip keeps a picture's
colors blue first. Some of what I put into a picture went in red first, so
red and blue swapped places:

  • PSET on a layer. Palette blue drew red, and red drew blue.
  • Drawing into a picture with SET IMAGE. Whatever was in the picture
    already, from a MEMORYBUFFER, LOADIMG or anything else, swapped its
    reds and blues the moment SET IMAGE started drawing into it. What
    SET IMAGE drew itself came out right, so this drew a red ring around a
    blue one:
SCREEN 320, 240
MEMORYBUFFER "rings", 64, 64
    CIRCLE (32, 32), 31, 1
END MEMORYBUFFER
SET IMAGE "rings"
CIRCLE (32, 32), 20, 1
SET LAYER 0
PUTIMAGE (0, 0), "rings"

Now both rings are blue. The same went for the colors you give with
SET PALETTE OFF, and they're right now too.

TEXT IN COLOR ON WINDOWS

On Windows, everything you PRINTed came out white, whatever COLOR said.
The background color worked, so COLOR 7, 4 gave you white words on red
instead of grey ones. I keep my letters white and paint them as I draw
them, and on Windows I forgot the paint.

Now text is the color you asked for, on Windows like everywhere else:

SCREEN 640, 480
PRINT "Hello World"
COLOR 4
PRINT "Hello World"
COLOR 7, 4
PRINT "Hello World"

VIDEOS IN THE RIGHT COLORS

A video's frames come from a different place on every kind of computer, and
some of them come blue first while others come red first. I took them all
to be blue first, so on Android, and in the web player, a video's reds and
blues were swapped: a red car drove by blue.

Now every frame says which way round it is, and I put it up the right way.

UUID

UUID gives you a name nothing else will ever have: 36 characters, like
3f2b8c1e-9d4a-4e7b-a6c5-1b2d3e4f5a6b, and a new one every time you read
it. It's handy for naming something that mustn't clash with anything else,
like a save slot or a player's record:

id$ = UUID
PRINT "Save slot: "; id$
SCREEN 320, 240
slot = uuid
PRINT "Save slot", slot

It comes from the computer's own randomness, not from RND, so RANDOMIZE
doesn't make it repeat. In Python, JavaScript and Lua, it's crash.uuid.

LETTERBOX OFF GOES EDGE TO EDGE

With LETTERBOX OFF, your game's screen sits on the same pixels it always
does, and the rest of the window is yours to draw on too. A map stopped at
the edge of the screen, though, even when there was more of it, and the
window beside it stayed black. And three kinds of layer did reach the edges
of the window, but stretched to its shape, so in a wide one a circle came
out an egg:

  • a layer made a plane, with AFFINE or PERSPECTIVE
  • a map layer made a plane
  • a layer with WRAPX or WRAPY
SCREEN 320, 240 !! letterbox(FALSE)
CREATE LAYER 1, 320, 240
SET LAYER 1
CIRCLE (160, 120), 80, 14
SET LAYER 1 !! plane("affine") zoom(1) wrap_x wrap_y
SET SCROLL 160, 120 !! angle(0)

Now all of them go edge to edge, the right shape: a map carries on past
your screen as far as it goes, a plane goes on to the window's edges, and a
wrapped layer tiles across the whole window. What's on your screen doesn't
move. A map or a wrapped layer with a SHADER of its own still stops at the
edge of the screen for now, the right shape.

JAVASCRIPT CALLBACKS RUN TO THE END

In JavaScript, a timer, an event or a render callback that came while your
program was waiting on an await could stop part-way, and the rest of your
program ran in the middle of it. So clearInterval could return, and the
interval still tick once more afterwards, about one time in fifteen:

let ticks = 0;
const ticking = setInterval(() => { ticks += 1; }, 5);
while (ticks < 2) await crash.yield();
clearInterval(ticking);
const stopped_at = ticks;
await crash.delay(40);
console.log(ticks === stopped_at);  // sometimes false!

Now a callback runs to the end before your program carries on, the way
JavaScript always promises, and that's true every time.

SHADER AND SQL BLOCKS IN COLOR

What's inside a SHADER or SQL block isn't BASIC, so the editor used to
color all of it like one long string. Now it's colored as what it is, in
the IDE, in the book and anywhere else I color code.

In a shader, USE, DEFAULT, WHEN DRAWING and SET COLOR are keywords,
and MIX, RGB, UV, IMAGE and the rest of what a shader gives you are
built-ins. In a SQL block, the USE and NULLS lines are colored like
BASIC, and the statement after them like SQL: SELECT and WHERE are
keywords, 'it''s' is a string, -- like this is a comment, and @least
is your parameter.

That fixes one thing in Crash BASIC 1 as well. There, a ' in a SQL block
started a BASIC comment, so a string like 'it''s' was colored as a comment
right to the end of the line.

THE HELP AND THE BOOKS KEEP UP

The help for SHADER used to say what a shader can do only in Crash BASIC 1.
Now the help in Crash BASIC 2, Python, JavaScript and Lua says it too: what
USE NUMBER, USE COLOR and USE IMAGE take, that a DEFAULT can't be
negative, what SCREEN counts, and which mistakes stop your program. The
help for SQL says the same about a NULLS value, in both Crash BASICs.

Every example in the help, in every language, now shows what it does when
you try it. Some never opened a screen, so what they printed went nowhere;
some made a sprite and never showed it; and some printed underneath a map,
which covered the words up. And they all use the same pictures, sounds and
maps as the books now: me running, the courtyard, a real explosion, rather
than a couple of colored squares.

A few CIRCLE examples gave TRUE where the fill color goes. The fill is a
color, so in palette mode that filled the circle blue, whatever its outline
was, and with SET PALETTE OFF it was a fill you couldn't see. They all give
a real color now.

There's a new book, Shaders with Crash, all about writing your own
shaders. Its cookbook has a recipe for a glow, where the bright parts of
the picture shine onto the dark around them. Making Games with Crash has a
new chapter, Databases, about keeping scores, saves and shops in a
database: what CLOSE does, where a database is kept, changing what's in
one, and NULLS. And its chapter on layers now shows sprites standing on a
floor that goes off into the distance, and jumping off it.

In the books, an example with a mistake in it on purpose now says so above
it, and the very next one is the same example put right, so you can run
into the mistake and then into the fix. A page opens on its first example
that works, instead of greeting you with an error.

All the books are in one place now too, the Crash Library: Learn Crash
BASIC
, Making Games with Crash, Shaders with Crash, and the Crash BASIC
2 reference, as a book of its own where every example runs with Try it.

A CORRECTION: PX

The shader cookbook said PX(x, y) divides by the screen's size, so that a
shader couldn't count its own picture's pixels without being told how big it
is. That was wrong. PX counts the pixels of the picture the shader is
drawing: the sprite's current frame, or the layer. So OWNTEXTURE(UV + PX(1.0, 0.0)) is always the next pixel to the right, however big the sprite is and
however far it's scaled. Only a USE IMAGE picture's size has to be passed
in. The cookbook's scrolling recipe uses PX now, and doesn't need its
picture's height any more.