mirror of
https://github.com/fairyglade/ly.git
synced 2026-06-22 07:22:00 +00:00
feat: LuaJIT Animations (#1001)
## What are the changes about? TL;DR:  Slaps the entire LuaJIT runtime onto Ly, allowing for the creation of custom dynamic animations like GameOfLife, ColorWave, Doom, etc. This PR adds the [ziglua](https://github.com/natecraddock/ziglua?ref=zig-0.16) dependency for its zig bindings and considerable buildtime config (mainly lua version selection). ### Example <video src="/attachments/3f91cf72-ae24-459c-8ef6-099f71e866fd" title="Screencast_20260519_172320" controls></video> ```lua ly.frame_delay = 5 local timer = 0 local clock = os.clock() local clock_diff = 0 function draw() timer = timer + 1 byte = string.byte(' ') clock_diff = os.clock() - clock clock = os.clock() timer = timer + clock_diff for x = 0, ly.width-1 do for y = 0, ly.height-1 do local xc = 0xFF if x < 255 then xc = ((x + math.floor(timer / 2)) * 3) % 255 else xc = 0 end local yc = 0xFF if y < 255 then yc = ((y) * 3) % 255 else yc = 0 end ly.putCell(byte, xc, bit.bor(xc, yc), x, y) end end end ``` ### The API The API that Ly gives to the user is minimal. A table is globally available, named `ly`, which provides the following: | Member | Purpose | |---------|---------| | `ly.width` & `ly.height` | Respective Width/Height from the `TerminalBuffer` | | `ly.putCell(byte, fg, bg, x, y)` | Literally `Cell.init(byte, fg, bg).put(x, y)`.| | `ly.clock()` | The current real-time, in microseconds. | ### Error Handling On a Lua Error, Ly won't quit but will instead paint the entire background red. The lua error in question can be found in the Ly log file and on-screen. ```log 2026-05-19 16:13:40 [err/Lua] Error (Cannot call draw()): attempt to call a nil value 2026-05-19 11:05:51 [err/Lua] Lua Error: ...dsammyt/programming/probe/ly/scratch/testConfig/test.lua:30: bad argument #1 to 'ipairs' (table expected, got number) ``` ## Pre-requisites - [X] I have tested & confirmed the changes work locally - [X] I have run `zig fmt` throughout my changes Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1001 Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
This commit is contained in:
119
res/example.lua
Normal file
119
res/example.lua
Normal file
@@ -0,0 +1,119 @@
|
||||
-- [[
|
||||
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case
|
||||
-- bouncing squares that change colors.
|
||||
--
|
||||
-- You are given the following `ly` table:
|
||||
-- {
|
||||
-- height: number -- The height of the terminal
|
||||
-- width: number -- The width of the terminal
|
||||
-- putCell(byte, fg, bg, x, y) -- Draw a cell.
|
||||
-- All arguments to this function are integers, and
|
||||
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
|
||||
-- If an argument cannot be converted to this range, it will throw
|
||||
-- an error.
|
||||
--
|
||||
-- For reference, the XY coordinates (0,0) draw a cell on the top-left
|
||||
-- of the terminal, where the positive-X axis moves right and the
|
||||
-- positive-Y axis moves down.
|
||||
--
|
||||
-- For arguments fg and bg: they are colors in the format
|
||||
-- 0xSSRRGGBB, where SS is for styling. See your
|
||||
-- config.ini for more details.
|
||||
--
|
||||
-- For the byte argument, you may use string.byte to fill this argument.
|
||||
--
|
||||
-- putCell(byte, fg, bg, x, y, w, h) -- Draw a rectangle.
|
||||
-- Arguments are the same as putCell except for w and h, which are also
|
||||
-- unsigned integers. The rectangle will be drawn from the top-left, with
|
||||
-- argument w extending it to the right and argument h extending downwards.
|
||||
--
|
||||
--
|
||||
-- putLabel(str, fg, bg, x, y) -- Draw text in argument str. See putCell()
|
||||
-- for info on the rest of the arguments.
|
||||
--
|
||||
-- clock() -- The time, in microseconds.
|
||||
-- }
|
||||
--
|
||||
-- A function named `draw()` must be declared in the script. This is ran every
|
||||
-- frame.
|
||||
--
|
||||
-- In addition to the base library, you are also given the following standard
|
||||
-- libraries:
|
||||
-- bit (A library exclusive to LuaJIT, see https://bitop.luajit.org/api.html)
|
||||
-- math
|
||||
-- string
|
||||
-- table
|
||||
--
|
||||
-- The std libraries io and debug are NOT included.
|
||||
--
|
||||
-- ]]
|
||||
|
||||
-- You should probably copy FPS and FPS_COUNT into any future LuaJIT animations
|
||||
-- you create.
|
||||
local FPS_COUNT = 40
|
||||
local function FPS()
|
||||
return (1/FPS_COUNT)*1000000
|
||||
end
|
||||
|
||||
|
||||
local SQUARE_WIDTH = 10
|
||||
local SQUARE_HEIGHT = 5
|
||||
|
||||
local SQUARE_COUNT = 25
|
||||
|
||||
local squares = {}
|
||||
|
||||
for i = 1, SQUARE_COUNT do
|
||||
local vx = 1
|
||||
local vy = 1
|
||||
if math.random(1, 2) == 2 then vx = -vx end
|
||||
if math.random(1, 2) == 2 then vy = -vy end
|
||||
squares[#squares+1] = {
|
||||
x = math.random(1, ly.width - SQUARE_WIDTH),
|
||||
y = math.random(1, ly.height - SQUARE_HEIGHT),
|
||||
vx = vx,
|
||||
vy = vy,
|
||||
color = math.random(0xFFFFFF)
|
||||
}
|
||||
end
|
||||
|
||||
local timer = ly.clock()
|
||||
local perf = ly.clock()
|
||||
|
||||
function draw()
|
||||
-- Rather than progressing the animation by frame, do it based on
|
||||
-- seconds, via ly.clock(). In this timeframe, you can update the animation
|
||||
-- state.
|
||||
-- DO NOT DRAW CELLS IN THIS TIMEFRAME. You will get flickering.
|
||||
|
||||
-- if this check passes, we can update the animation
|
||||
if timer + FPS() < ly.clock() then
|
||||
for i, v in ipairs(squares) do
|
||||
v.x = v.x + v.vx
|
||||
v.y = v.y + v.vy
|
||||
if v.x == 0 then
|
||||
v.vx = 1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.x + SQUARE_WIDTH >= ly.width-1 then
|
||||
v.vx = -1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.y == 0 then
|
||||
v.vy = 1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.y + SQUARE_HEIGHT >= ly.height-1 then
|
||||
v.vy = -1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
end
|
||||
timer = ly.clock()
|
||||
end
|
||||
|
||||
|
||||
for i, v in ipairs(squares) do
|
||||
ly.putRect(string.byte(' '), 0, v.color, v.x, v.y, SQUARE_WIDTH, SQUARE_HEIGHT)
|
||||
end
|
||||
|
||||
local new_perf = ly.clock()
|
||||
local str = "FT: "..((new_perf - perf) / 1000).."ms"
|
||||
ly.putLabel(str , 0x00FFFFFF, 0, (ly.width/2) - (string.len(str)/2), ly.height-1)
|
||||
perf = new_perf
|
||||
end
|
||||
Reference in New Issue
Block a user