The new algorithm is a lot less code, slightly faster, and results
in a smaller binary (-40 KiB on rg99).
The previous algorithm filled all the pixels around every solid pixel.
The new algorithm only fills pixels that will be visible.
We first collect the outline pixels into an array (which may contain a
small amount of duplicates). Then, we render the entire array in a
single loop. This turns out to be slightly faster than rendering inline,
at the cost of ~4 KiB of stack (basically free).
To collect the pixels, we go through the CLX sprite, keeping track
of the solid runs in the current row, and the filled pixels on the line
above and the line below.
To be able to quickly test the pixels above and below, we introduce a
new data structure, `StaticBitVector`. It is similar to a bitset,
except the size is determined at runtime (capacity is fixed),
and it supports quick updates of entire subspans.
We know that length is never 0.
Letting the compiler know that allows it to optimize one instruction
away.
Moreover, for Fill runs, we also know that the length is at least 2.
Inlines blit command parsing.
We previously had blit commands because we supported rendering multiple
formats (CEL, CL2, CLX) but now we only ever render CLX, so this is
no longer necessary.
We were previously not setting it all which was incorrect but did not
cause any issues because we had not used it. We do check scancode for
the debug console key. This caused a sanitizer warning when running the
demo in debug mode.
1. `Events` global is replaced with `require('devilutionx.events')`.
2. Table is simplified and documented.
3. `On` removed from the event names as it was a bit redundant.
4. Functions are camelCase for consistency (e.g. `add` instead of
`Add`).
Example script:
```lua
local events = require("devilutionx.events")
local render = require("devilutionx.render")
local message = require("devilutionx.message")
local function greet()
message("Hello from " .. _VERSION)
print("Hello from ", _VERSION)
end
events.GameStart.add(greet)
local function drawGreet()
render.string("Hello from " .. _VERSION, 10, 40)
end
events.GameDrawComplete.add(drawGreet)
```
Implements a `require` function that supports built-in modules like so:
```lua
local log = require('devilutionx.log')
```
It falls back to reading from assets, so this loads `lua/user.lua`:
```lua
local user = require('lua.user')
```
The bytecode for the asset scripts is cached, in case we want to later
support multiple isolated environments.
There may be a simpler or better way to do this.
It's good enough for now until someone more knowledgeable
about Lua comes along.
Scrolling: PageUp/Down and mouse wheel.
History navigation:
* Up/Down navigates the input history.
* Shift+Up/Down navigates the output history (allowing us to copy/paste the
outputs, I imagine this will be very handy).
* Duplicates are skipped.
Enabled only in Debug mode.
Runs Lua similar to the `lua` CLI.
Supports multiline input with Shift+Enter.
Missing features:
1. Scrollback.
2. Input history on up/down.
Open with backtick, close with Esc.
```lua
local render = devilutionx.render
local function drawGreet ()
render.string("Hello from " .. _VERSION, 10, 40)
end
Events.OnGameDrawComplete.Add(drawGreet)
```
4 options args are a bit unwieldy, especially when you want
to pass only the first and the last one.
With a struct, there is no need to specify the default values
for the args in between.