Hi, Joe!
Making games in the Mini Micro and writing code in the MiniScript is always interesting and exciting!
Thank you for the invitation, I will try to be on this fun )
Sorry, but this is a very strange code)
That's how it works:
--script: lua
attribs={"spam","toast","blah"}
--get a random attribute
function get_attrib()
trace("getting attribute...",14)
local random=math.random(1,3)
local attrib=attribs[random]
trace("gotten attribute is...")
trace(attrib,9)
trace("")
return attrib
end
--get an object with three attributes
function get_obj()
trace("building object...",8)
local z={
get_attrib(),
get_attrib(),
get_attrib()
}
trace("objects returned attributes...")
trace(z[1],6)
trace(z[2],6)
trace(z[3],6)
trace("")
return z
end
function TIC()
--test get_attrib() return directly
trace("getting a test attribute",8)
temp=get_attrib()
trace("test attribute...")
trace(temp,2)
trace("")
--log an object
local objs = get_obj()
--iterate through objects and test for
--certain attributes
trace("started object testing",8)
for i,v in pairs(objs) do
trace("object attributes are")
trace(v,6)
trace("")
end
trace("end of process",6)
exit()
end"So basically using 2-bit instead of 4-bit sprites does not make any difference in the number of sprites you have available. The number is always 512 (256 FG + 256 BG)." - this is not true.
I updated the example - https://tic.computer/play?cart=100
Now in the example the difference is more noticeable:
4-bit sprite takes 12 cells (4x3) -> 32x24 pixels -> 768 pixels
2 bit takes 6 cells (6х1) -> 48x8 pixels -> 384 pixels
But you lose the ability to view / edit graphics in the built-in editor and and API functions are not suitable for printing to the screen.
My example does the following:
1. I have a painted sprite the size of 4x3 cells, it is drawn in BG.
I put it on the screen at position 0,0. The rest of the space BG and FG is empty.
2. Now I read 2-bit pixel values from the screen, combine two values into one 4-bit value and save it in a table
3. I write down all the values from the received table into memory. For clarity, I wrote them down in the FG area, but this area can be any.
4. Now I show how you can display a 2-bit image.
Also, for clarity, I used the palette of the 2-bit CGA palette
Here you can see the animation
-- title: 2-bit sprite
-- author: Al Rado
-- desc: shows how convert native TIC-80 4-bit sprite to 2-bit
-- script: lua
-- input: gamepad
-- pal: 00000000ffffff00ffffffff000000000000000000000000000000000000000000000000000000000000000000000000
SPRITE_ADDR=0x4000*2
NIBBLES_IN_SPR=64
SPR_IX=256
ADDR=SPRITE_ADDR+NIBBLES_IN_SPR*SPR_IX
SPR_W=4*8
SPR_H=3*8
-- convert native sprite to 2-bit sprite and save to memory
cls()
spr(0,0,0,-1,1,0,0,4,3)
local nibbles={}
for i=0,SPR_W*SPR_H-1 do
nibbles[#nibbles+1]=pix(i%SPR_W,i/SPR_W)
end
for i=1,#nibbles,2 do
local first=nibbles[i] << 2
local second=nibbles[i+1]
poke4(ADDR+i//2, first+second)
end
-- draw 2-bit screen from memory
local posX=50
local posY=50
for i=1,SPR_W*SPR_H,2 do
local val=peek4(ADDR+i//2)
local first=val >> 2
local second=val - (first << 2)
pix(posX+(i-1)%SPR_W,posY+(i-1)/SPR_W,first)
pix(posX+(i)%SPR_W,posY+(i)/SPR_W,second)
end
function TIC()
-- not implemented
end
I wrote an example of how you can store 2-bit sprites that take up half the amount of memory http://tic.computer/play?cart=100
You can use LZW-compressor for graphics and save a lot of space, here is an example https://tic.computer/play?cart=86
Lua collision-detection library for axis-aligned rectangles. Its main features are:
See more on github
The code turned out to be quite large, so the link to the cartridge
Added ZX-Spectrum color palettes: Pulsar, Alone, Orthodox, Electroscale
For use in cartridge metadata:
-- ZX-Spectrum Orthodox palette
-- pal: 0000000000cda70000a700cd00b70000b7cda7b700a7b7cd0000000000ffd00000d000ff00e40000e4ffd0e400d0e4ff
-- ZX-Spectrum Pulsar palette
-- pal: 0000000000cdcd0000cd00cd00cd0000cdcdcdcd00cdcdcd0000000000ffff0000ff00ff00ff0000ffffffff00ffffff
-- ZX-Spectrum Alone palette
-- pal: 0000000000a0a00000a000a000a00000a0a0a0a000a0a0a00000000000ffff0000ff00ff00ff0000ffffffff00ffffff
-- ZX-Spectrum Electroscale palette
-- pal: 3e414c4e515f5e62736e73867e839a8e94ad9ea4c1aeb5d43e414c525564666a7c7a7f948e93ada2a8c5b5bcddc9d1f5
For use in config.lua:
local ZXORTHO =
"000000".."0000cd".."a70000".."a700cd".."00b700".."00b7cd".."a7b700".."a7b7cd"..
"000000".."0000ff".."d00000".."d000ff".."00e400".."00e4ff".."d0e400".."d0e4ff"
local ZXPULSAR =
"000000".."0000cd".."cd0000".."cd00cd".."00cd00".."00cdcd".."cdcd00".."cdcdcd"..
"000000".."0000ff".."ff0000".."ff00ff".."00ff00".."00ffff".."ffff00".."ffffff"
local ZXALONE =
"000000".."0000a0".."a00000".."a000a0".."00a000".."00a0a0".."a0a000".."a0a0a0"..
"000000".."0000ff".."ff0000".."ff00ff".."00ff00".."00ffff".."ffff00".."ffffff"
local ZXESCALE =
"3e414c".."4e515f".."5e6273".."6e7386".."7e839a".."8e94ad".."9ea4c1".."aeb5d4"..
"3e414c".."525564".."666a7c".."7a7f94".."8e93ad".."a2a8c5".."b5bcdd".."c9d1f5"
I re-order colors by luminance, maybe you can do better. CGA palette by Trelemar.
Re-ordered palettes for use in config.lua:
local VIC20 =
"000000".."772d26".."42348b".."a85fb4".."b66862".."7e70ca".."a8734a".."559e4a"..
"e99df5".."e9b287".."bdcc71".."85d4dc".."92df87".."c5ffff".."ffffb0".."ffffff"
local CGA =
"000000".."aa0000".."0000aa".."555555".."aa5500".."00aa00".."ff5555".."aaaaaa"..
"5555ff".."aa00aa".."00aaaa".."55ff55".."ff55ff".."55ffff".."ffff55".."ffffff"
local SLIFE =
"000000".."122615".."3f2811".."7a2222".."513155".."d13b27".."286fb8".."5d853a"..
"cc8218".."e07f8a".."9b8bff".."68c127".."c7b581".."b3e868".."a8e4d4".."ffffff"
local JMP =
"000000".."191028".."833129".."453e78".."216c4b".."dc534b".."7664fe".."d365c8"..
"46af45".."e18d79".."afaab9".."d6b97b".."9ec2e8".."a1d685".."e9d8a1".."f5f4eb"
local CGARNE =
"000000".."2234d1".."5c2e78".."8a3622".."5e606e".."0c7e45".."e23d69".."aa5c3d"..
"4c81fb".."44aacc".."eb8a60".."b5b5b5".."6cd947".."7be2f9".."ffd93f".."ffffff"
local PSYG =
"000000".."1b1e29".."003308".."362747".."084a3c".."443f41".."a2324e".."52524c"..
"546a00".."736150".."64647c".."516cbf".."77785b".."e08b79".."9ea4a7".."cbe8f7"
local EROGE =
"0d080d".."2a2349".."4f2b24".."7d3840".."32535f".."825b31".."4180a0".."c16c5b"..
"c59154".."7bb24e".."74adbb".."e89973".."bebbb2".."f0bd77".."fbdf9b".."fff9e4"
local EISLAND =
"051625".."794765".."686086".."567864".."ca657e".."868691".."8184ab".."cc8d86"..
"7ea788".."39d4b9".."8dbcd2".."9dc085".."edc38d".."e6d1d1".."f5e17a".."f6f6bf"
Color palettes as is. Can be used as a palette for the game - the order of colors corresponds to the original.
Reordered colors in palettes for use in TIC-80 config is coming soon =)
-- COMMODORE VIC-20 PALETTE
-- pal: 000000ffffffa8734ae9b287772d26b6686285d4dcc5ffffa85fb4e99df5559e4a92df8742348b7e70cabdcc71ffffb0
-- STILL LIFE PALETTE
-- pal: 3f28117a2222d13b27e07f8a5d853a68c127b3e868122615513155286fb89b8bffa8e4d4cc8218c7b581000000ffffff
-- JAPANESE MACHINE PALETTE PALETTE
-- pal: 00000019102846af45a1d685453e787664fe8331299ec2e8dc534be18d79d6b97be9d8a1216c4bd365c8afaab9f5f4eb
-- CGARNE PALETTE
-- pal: 0000005e606e2234d10c7e455c2e78b5b5b5FFFFFFffd93f7be2f98a36224c81fb44aacceb8a60aa5c3d6cd947e23d69
-- PSYGNOSIA PALETTE
-- pal: 0000001b1e29362747443f4152524c64647c73615077785b9ea4a7cbe8f7e08b79a2324e003308084a3c546a00516cbf
-- COLOR GRAPHICS ADAPTER PALETTE
-- pal: 000000555555AAAAAAFFFFFF0000AA5555FF00AA0055FF5500AAAA55FFFFAA0000FF5555AA00AAFF55FFAA5500FFFF55
-- EROGE COPPER PALETTE
-- pal: 0d080d4f2b24825b31c59154f0bd77fbdf9bfff9e4bebbb27bb24e74adbb4180a032535f2a23497d3840c16c5be89973
-- EASTER ISLAND PALETTE
-- pal: f6f6bfe6d1d1868691794765f5e17aedc38dcc8d86ca657e39d4b98dbcd28184ab6860869dc0857ea788567864051625
Thanks zep@lexaloffle for great examples!
Pico-8 Lua code by zep@lexaloffle:
r=64
t=0
::a::
cls()
for y=-r,r,3 do
for x=-r,r,2 do
z=cos(sqrt(x*x+y*y*2)/40-t)*6
pset(r+x,r+y-z,6)
end
end
flip()
t+=2/r
goto a
TIC-80 Lua code by me:
-- title: `ripples` demo by zep@lexaloffle
-- author: Al Rado 24.03.2017
-- desc: ported from Pico-8 =)
-- script: lua
-- pal: PICO8
r=120
t=0
function TIC()
cls()
for y=-r,r,3 do
for x=-r,r,2 do
z=cos(math.sqrt(x*x+y*y*2)/40-t)*6
pix(r+x,r+y-z-52,10)
end
end
t=t+2/r
end
-- PICO-8 cos
function cos(a)
return math.cos(2*math.pi*a)
end
'Lume' is collection of functions for Lua, geared towards game development.
The github page for 'Lume' is over here.
-- title: Lume demo
-- author: Al Rado 12.03.2017
-- desc: Simple demo
-- script: lua
-- input: gamepad
-- lume
--
-- Copyright (c) 2016 rxi
--
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
--
local lume = { _version = "2.2.3" }
local pairs, ipairs = pairs, ipairs
local type, assert, unpack = type, assert, unpack or table.unpack
local tostring, tonumber = tostring, tonumber
local math_floor = math.floor
local math_ceil = math.ceil
local math_atan2 = math.atan2 or math.atan
local math_sqrt = math.sqrt
local math_abs = math.abs
local noop = function()
end
local identity = function(x)
return x
end
local patternescape = function(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
end
local absindex = function(len, i)
return i < 0 and (len + i + 1) or i
end
local iscallable = function(x)
if type(x) == "function" then return true end
local mt = getmetatable(x)
return mt and mt.__call ~= nil
end
local getiter = function(x)
if lume.isarray(x) then
return ipairs
elseif type(x) == "table" then
return pairs
end
error("expected table", 3)
end
local iteratee = function(x)
if x == nil then return identity end
if iscallable(x) then return x end
if type(x) == "table" then
return function(z)
for k, v in pairs(x) do
if z[k] ~= v then return false end
end
return true
end
end
return function(z) return z[x] end
end
function lume.clamp(x, min, max)
return x < min and min or (x > max and max or x)
end
function lume.round(x, increment)
if increment then return lume.round(x / increment) * increment end
return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
end
function lume.sign(x)
return x < 0 and -1 or 1
end
function lume.lerp(a, b, amount)
return a + (b - a) * lume.clamp(amount, 0, 1)
end
function lume.smooth(a, b, amount)
local t = lume.clamp(amount, 0, 1)
local m = t * t * (3 - 2 * t)
return a + (b - a) * m
end
function lume.pingpong(x)
return 1 - math_abs(1 - x % 2)
end
function lume.distance(x1, y1, x2, y2, squared)
local dx = x1 - x2
local dy = y1 - y2
local s = dx * dx + dy * dy
return squared and s or math_sqrt(s)
end
function lume.angle(x1, y1, x2, y2)
return math_atan2(y2 - y1, x2 - x1)
end
function lume.vector(angle, magnitude)
return math.cos(angle) * magnitude, math.sin(angle) * magnitude
end
function lume.random(a, b)
if not a then a, b = 0, 1 end
if not b then b = 0 end
return a + math.random() * (b - a)
end
function lume.randomchoice(t)
return t[math.random(#t)]
end
function lume.weightedchoice(t)
local sum = 0
for _, v in pairs(t) do
assert(v >= 0, "weight value less than zero")
sum = sum + v
end
assert(sum ~= 0, "all weights are zero")
local rnd = lume.random(sum)
for k, v in pairs(t) do
if rnd < v then return k end
rnd = rnd - v
end
end
function lume.isarray(x)
return (type(x) == "table" and x[1] ~= nil) and true or false
end
function lume.push(t, ...)
local n = select("#", ...)
for i = 1, n do
t[#t + 1] = select(i, ...)
end
return ...
end
function lume.remove(t, x)
local iter = getiter(t)
for i, v in iter(t) do
if v == x then
if lume.isarray(t) then
table.remove(t, i)
break
else
t[i] = nil
break
end
end
end
return x
end
function lume.clear(t)
local iter = getiter(t)
for k in iter(t) do
t[k] = nil
end
return t
end
function lume.extend(t, ...)
for i = 1, select("#", ...) do
local x = select(i, ...)
if x then
for k, v in pairs(x) do
t[k] = v
end
end
end
return t
end
function lume.shuffle(t)
local rtn = {}
for i = 1, #t do
local r = math.random(i)
if r ~= i then
rtn[i] = rtn[r]
end
rtn[r] = t[i]
end
return rtn
end
function lume.sort(t, comp)
local rtn = lume.clone(t)
if comp then
if type(comp) == "string" then
table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
else
table.sort(rtn, comp)
end
else
table.sort(rtn)
end
return rtn
end
function lume.array(...)
local t = {}
for x in ... do t[#t + 1] = x end
return t
end
function lume.each(t, fn, ...)
local iter = getiter(t)
if type(fn) == "string" then
for _, v in iter(t) do v[fn](v, ...) end
else
for _, v in iter(t) do fn(v, ...) end
end
return t
end
function lume.map(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
for k, v in iter(t) do rtn[k] = fn(v) end
return rtn
end
function lume.all(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for _, v in iter(t) do
if not fn(v) then return false end
end
return true
end
function lume.any(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for _, v in iter(t) do
if fn(v) then return true end
end
return false
end
function lume.reduce(t, fn, first)
local acc = first
local started = first and true or false
local iter = getiter(t)
for _, v in iter(t) do
if started then
acc = fn(acc, v)
else
acc = v
started = true
end
end
assert(started, "reduce of an empty table with no first value")
return acc
end
function lume.set(t)
local rtn = {}
for k in pairs(lume.invert(t)) do
rtn[#rtn + 1] = k
end
return rtn
end
function lume.filter(t, fn, retainkeys)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
if retainkeys then
for k, v in iter(t) do
if fn(v) then rtn[k] = v end
end
else
for _, v in iter(t) do
if fn(v) then rtn[#rtn + 1] = v end
end
end
return rtn
end
function lume.reject(t, fn, retainkeys)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
if retainkeys then
for k, v in iter(t) do
if not fn(v) then rtn[k] = v end
end
else
for _, v in iter(t) do
if not fn(v) then rtn[#rtn + 1] = v end
end
end
return rtn
end
function lume.merge(...)
local rtn = {}
for i = 1, select("#", ...) do
local t = select(i, ...)
local iter = getiter(t)
for k, v in iter(t) do
rtn[k] = v
end
end
return rtn
end
function lume.concat(...)
local rtn = {}
for i = 1, select("#", ...) do
local t = select(i, ...)
if t ~= nil then
local iter = getiter(t)
for _, v in iter(t) do
rtn[#rtn + 1] = v
end
end
end
return rtn
end
function lume.find(t, value)
local iter = getiter(t)
for k, v in iter(t) do
if v == value then return k end
end
return nil
end
function lume.match(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for k, v in iter(t) do
if fn(v) then return v, k end
end
return nil
end
function lume.count(t, fn)
local count = 0
local iter = getiter(t)
if fn then
fn = iteratee(fn)
for _, v in iter(t) do
if fn(v) then count = count + 1 end
end
else
if lume.isarray(t) then
return #t
end
for _ in iter(t) do count = count + 1 end
end
return count
end
function lume.slice(t, i, j)
i = i and absindex(#t, i) or 1
j = j and absindex(#t, j) or #t
local rtn = {}
for x = i < 1 and 1 or i, j > #t and #t or j do
rtn[#rtn + 1] = t[x]
end
return rtn
end
function lume.first(t, n)
if not n then return t[1] end
return lume.slice(t, 1, n)
end
function lume.last(t, n)
if not n then return t[#t] end
return lume.slice(t, -n, -1)
end
function lume.invert(t)
local rtn = {}
for k, v in pairs(t) do rtn[v] = k end
return rtn
end
function lume.pick(t, ...)
local rtn = {}
for i = 1, select("#", ...) do
local k = select(i, ...)
rtn[k] = t[k]
end
return rtn
end
function lume.keys(t)
local rtn = {}
local iter = getiter(t)
for k in iter(t) do rtn[#rtn + 1] = k end
return rtn
end
function lume.clone(t)
local rtn = {}
for k, v in pairs(t) do rtn[k] = v end
return rtn
end
function lume.fn(fn, ...)
assert(iscallable(fn), "expected a function as the first argument")
local args = { ... }
return function(...)
local a = lume.concat(args, { ... })
return fn(unpack(a))
end
end
function lume.once(fn, ...)
local f = lume.fn(fn, ...)
local done = false
return function(...)
if done then return end
done = true
return f(...)
end
end
local memoize_fnkey = {}
local memoize_nil = {}
function lume.memoize(fn)
local cache = {}
return function(...)
local c = cache
for i = 1, select("#", ...) do
local a = select(i, ...) or memoize_nil
c[a] = c[a] or {}
c = c[a]
end
c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
return unpack(c[memoize_fnkey])
end
end
function lume.combine(...)
local n = select('#', ...)
if n == 0 then return noop end
if n == 1 then
local fn = select(1, ...)
if not fn then return noop end
assert(iscallable(fn), "expected a function or nil")
return fn
end
local funcs = {}
for i = 1, n do
local fn = select(i, ...)
if fn ~= nil then
assert(iscallable(fn), "expected a function or nil")
funcs[#funcs + 1] = fn
end
end
return function(...)
for _, f in ipairs(funcs) do f(...) end
end
end
function lume.call(fn, ...)
if fn then
return fn(...)
end
end
function lume.time(fn, ...)
local start = time()
local rtn = {fn(...)}
return (time() - start), unpack(rtn)
end
local lambda_cache = {}
function lume.lambda(str)
if not lambda_cache[str] then
local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
assert(args and body, "bad string lambda")
local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
lambda_cache[str] = lume.dostring(s)
end
return lambda_cache[str]
end
local serialize
local serialize_map = {
[ "boolean" ] = tostring,
[ "nil" ] = tostring,
[ "string" ] = function(v) return string.format("%q", v) end,
[ "number" ] = function(v)
if v ~= v then return "0/0" -- nan
elseif v == 1 / 0 then return "1/0" -- inf
elseif v == -1 / 0 then return "-1/0" end -- -inf
return tostring(v)
end,
[ "table" ] = function(t, stk)
stk = stk or {}
if stk[t] then error("circular reference") end
local rtn = {}
stk[t] = true
for k, v in pairs(t) do
rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
end
stk[t] = nil
return "{" .. table.concat(rtn, ",") .. "}"
end
}
setmetatable(serialize_map, {
__index = function(_, k) error("unsupported serialize type: " .. k) end
})
serialize = function(x, stk)
return serialize_map[type(x)](x, stk)
end
function lume.serialize(x)
return serialize(x)
end
function lume.deserialize(str)
return lume.dostring("return " .. str)
end
function lume.split(str, sep)
if not sep then
return lume.array(str:gmatch("([%S]+)"))
else
assert(sep ~= "", "empty separator")
local psep = patternescape(sep)
return lume.array((str..sep):gmatch("(.-)("..psep..")"))
end
end
function lume.trim(str, chars)
if not chars then return str:match("^[%s]*(.-)[%s]*$") end
chars = patternescape(chars)
return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
end
function lume.format(str, vars)
if not vars then return str end
local f = function(x)
return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
end
return (str:gsub("{(.-)}", f))
end
function lume.dostring(str)
return assert((loadstring or load)(str))()
end
function lume.uuid()
local fn = function(x)
local r = math.random(16) - 1
r = (x == "x") and (r + 1) or (r % 4) + 9
return ("0123456789abcdef"):sub(r, r)
end
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
end
local ripairs_iter = function(t, i)
i = i - 1
local v = t[i]
if v then return i, v end
end
function lume.ripairs(t)
return ripairs_iter, t, (#t + 1)
end
function lume.color(str, mul)
mul = mul or 1
local r, g, b, a
r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
if r then
r = tonumber(r, 16) / 0xff
g = tonumber(g, 16) / 0xff
b = tonumber(b, 16) / 0xff
a = 1
elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
local f = str:gmatch("[%d.]+")
r = (f() or 0) / 0xff
g = (f() or 0) / 0xff
b = (f() or 0) / 0xff
a = f() or 1
else
error(("bad color string '%s'"):format(str))
end
return r * mul, g * mul, b * mul, a * mul
end
function lume.rgba(color)
local a = math_floor((color / 16777216) % 256)
local r = math_floor((color / 65536) % 256)
local g = math_floor((color / 256) % 256)
local b = math_floor((color) % 256)
return r, g, b, a
end
local chain_mt = {}
chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
function(fn)
return function(self, ...)
self._value = fn(self._value, ...)
return self
end
end)
chain_mt.__index.result = function(x) return x._value end
function lume.chain(value)
return setmetatable({ _value = value }, chain_mt)
end
setmetatable(lume, {
__call = function(_, ...)
return lume.chain(...)
end
})
-----------------------------------
-- demo code
SCR_WIDTH,SCR_HEIGHT = 240, 136
BEHIND_SCR_POS=-100
FONT_HEIGHT=6
MAX_COLORS=15
NAME="Lume"
DESCR="Lua functions geared towards gamedev"
descrWords=lume.split(DESCR)
radius=0
angle=45
STEP=0.03
SMOOTH_STEP=0.01
posX=0
delta=0
dir=1
word=""
wordX,wordY=0
wordIx=0
rectShift=8
border=lume.lambda "color -> poke(0x3FF8,color)"
function TIC()
cls(11)
border(11)
angle=(angle+STEP)%360
radius=(radius+STEP)%50
local x,y = lume.vector(angle,20+radius)
local scrX=x+SCR_WIDTH/2
local scrY=y+SCR_HEIGHT/2
local col = lume.pingpong(angle)*MAX_COLORS
local len = getLen(NAME)
rect(scrX-rectShift/2,scrY-rectShift/2,len+rectShift,FONT_HEIGHT+rectShift,col)
print(NAME,scrX,scrY,MAX_COLORS-col)
delta=delta+SMOOTH_STEP
if(delta >= 1) then
dir=-dir
delta=0
end
len=getLen(DESCR)
toPos=dir>0 and SCR_WIDTH-len or 0
posX=lume.smooth(posX,toPos,delta%1)
print(DESCR,posX,100,col)
if(delta==0) then
wordIx=wordIx%#descrWords+1
word=descrWords[wordIx]
wordX=msgRndX(word)
wordY=msgRndY(word)
end
print(word,wordX,wordY,3)
local rndWord=lume.randomchoice(descrWords)
print(rndWord,msgRndX(rndWord),msgRndY(rndWord),14)
end
function msgRndX(msg)
return lume.random(0,SCR_WIDTH-getLen(msg))
end
function msgRndY(msg)
return lume.random(0,SCR_HEIGHT-FONT_HEIGHT)
end
function getLen(msg)
return print(msg,BEHIND_SCR_POS,BEHIND_SCR_POS)
end
A fast, lightweight tweening library for Lua with easing functions and the ability to group tweens together.
The github page for flux is over here.
-- title: Tween demo
-- author: Al Rado 07.03.2017
-- desc: simple tween demo
-- script: lua
-- input: gamepad
-- flux
-- Copyright (c) 2016 rxi
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
local flux = { _version = "0.1.5" }
flux.__index = flux
flux.tweens = {}
flux.easing = { linear = function(p) return p end }
local easing = {
quad = "p * p",
cubic = "p * p * p",
quart = "p * p * p * p",
quint = "p * p * p * p * p",
expo = "2 ^ (10 * (p - 1))",
sine = "-math.cos(p * (math.pi * .5)) + 1",
circ = "-(math.sqrt(1 - (p * p)) - 1)",
back = "p * p * (2.7 * p - 1.7)",
elastic = "-(2^(10 * (p - 1)) * math.sin((p - 1.075) * (math.pi * 2) / .3))"
}
local makefunc = function(str, expr)
local load = loadstring or load
return load("return function(p) " .. str:gsub("%$e", expr) .. " end")()
end
for k, v in pairs(easing) do
flux.easing[k .. "in"] = makefunc("return $e", v)
flux.easing[k .. "out"] = makefunc([[
p = 1 - p
return 1 - ($e)
]], v)
flux.easing[k .. "inout"] = makefunc([[
p = p * 2
if p < 1 then
return .5 * ($e)
else
p = 2 - p
return .5 * (1 - ($e)) + .5
end
]], v)
end
local tween = {}
tween.__index = tween
local function makefsetter(field)
return function(self, x)
local mt = getmetatable(x)
if type(x) ~= "function" and not (mt and mt.__call) then
error("expected function or callable", 2)
end
local old = self[field]
self[field] = old and function() old() x() end or x
return self
end
end
local function makesetter(field, checkfn, errmsg)
return function(self, x)
if checkfn and not checkfn(x) then
error(errmsg:gsub("%$x", tostring(x)), 2)
end
self[field] = x
return self
end
end
tween.ease = makesetter("_ease",
function(x) return flux.easing[x] end,
"bad easing type '$x'")
tween.delay = makesetter("_delay",
function(x) return type(x) == "number" end,
"bad delay time; expected number")
tween.onstart = makefsetter("_onstart")
tween.onupdate = makefsetter("_onupdate")
tween.oncomplete = makefsetter("_oncomplete")
function tween.new(obj, time, vars)
local self = setmetatable({}, tween)
self.obj = obj
self.rate = time > 0 and 1 / time or 0
self.progress = time > 0 and 0 or 1
self._delay = 0
self._ease = "quadout"
self.vars = {}
for k, v in pairs(vars) do
if type(v) ~= "number" then
error("bad value for key '" .. k .. "'; expected number")
end
self.vars[k] = v
end
return self
end
function tween:init()
for k, v in pairs(self.vars) do
local x = self.obj[k]
if type(x) ~= "number" then
error("bad value on object key '" .. k .. "'; expected number")
end
self.vars[k] = { start = x, diff = v - x }
end
self.inited = true
end
function tween:after(...)
local t
if select("#", ...) == 2 then
t = tween.new(self.obj, ...)
else
t = tween.new(...)
end
t.parent = self.parent
self:oncomplete(function() flux.add(self.parent, t) end)
return t
end
function tween:stop()
flux.remove(self.parent, self)
end
function flux.group()
return setmetatable({}, flux)
end
function flux:to(obj, time, vars)
return flux.add(self, tween.new(obj, time, vars))
end
function flux:update(deltatime)
for i = #self, 1, -1 do
local t = self[i]
if t._delay > 0 then
t._delay = t._delay - deltatime
else
if not t.inited then
flux.clear(self, t.obj, t.vars)
t:init()
end
if t._onstart then
t._onstart()
t._onstart = nil
end
t.progress = t.progress + t.rate * deltatime
local p = t.progress
local x = p >= 1 and 1 or flux.easing[t._ease](p)
for k, v in pairs(t.vars) do
t.obj[k] = v.start + x * v.diff
end
if t._onupdate then t._onupdate() end
if p >= 1 then
flux.remove(self, i)
if t._oncomplete then t._oncomplete() end
end
end
end
end
function flux:clear(obj, vars)
for t in pairs(self[obj]) do
if t.inited then
for k in pairs(vars) do t.vars[k] = nil end
end
end
end
function flux:add(tween)
-- Add to object table, create table if it does not exist
local obj = tween.obj
self[obj] = self[obj] or {}
self[obj][tween] = true
-- Add to array
table.insert(self, tween)
tween.parent = self
return tween
end
function flux:remove(x)
if type(x) == "number" then
-- Remove from object table, destroy table if it is empty
local obj = self[x].obj
self[obj][self[x]] = nil
if not next(self[obj]) then self[obj] = nil end
-- Remove from array
self[x] = self[#self]
return table.remove(self)
end
for i, v in ipairs(self) do
if v == x then
return flux.remove(self, i)
end
end
end
local Tween = {
to = function(...) return flux.to(flux.tweens, ...) end,
update = function(...) return flux.update(flux.tweens, ...) end,
remove = function(...) return flux.remove(flux.tweens, ...) end,
}
setmetatable(Tween, flux)
-------------------------------------------------------------------
-- demo code
TYPES={"elasticin", "elasticout", "elasticinout", "linear", "quadin", "quadout", "quadinout", "cubicin", "cubicout", "cubicinout", "quartin", "quartout",
"quartinout", "quintin", "quintout", "quintinout", "expoin", "expoout", "expoinout", "sinein", "sineout", "sineinout",
"circin", "circout", "circinout", "backin", "backout", "backinout"}
TIME=1500
START_Y=15
FINISH_Y=50
START_COLOR=0
FINISH_COLOR=15
DELAY=500
local lastTime=0
local counter=0
local easeType = TYPES[counter]
local tween=nil
logo={
x=104,
y=START_Y,
color=START_COLOR,
msg="easing type: ",
easeType="",
draw = function ()
spr(1+(time()%1000)/500,logo.x,logo.y,-1,4)
local fullMsg=counter..". "..logo.msg..logo.easeType
local msgX=(240-print(fullMsg,-100,-100))/2
print(fullMsg,msgX,136-logo.y,logo.color)
end
}
function startTween()
if(tween ~=nil) then tween:stop() end
counter = counter+1
counter = counter>#TYPES and 1 or counter
easeType = TYPES[counter]
logo.easeType = easeType
tween=Tween.to(logo,TIME,{y=FINISH_Y,color=FINISH_COLOR}):ease(easeType):onupdate(logo.draw):delay(DELAY)
:after(logo,TIME,{y=START_Y,color=START_COLOR}):ease(easeType):onupdate(logo.draw):oncomplete(startTween)
end
startTween()
function TIC()
cls(12)
poke(0x3FF8,logo.color)
print("Flux - tweening library",55,4,logo.color)
Tween.update(time()-lastTime)
lastTime=time()
end
Thanks friends!
Nicholas Musurca@musurca makes life easier ) Thanks to him!
When porting a lot of useful stuff here: https://github.com/musurca/pico2tic/
Pico-8 Lua code by zep@lexaloffle:
t=0 ::a:: cls() t+=1/9 for i=0,1,0.1 do x=64+cos(i+t)*48 y=64+sin(i+t)*24 circfill(x,y,y/12,12+y%4) end ?"\146 rad bot! \146",40,60 flip() goto a
TIC-80 Lua code by me:
-- title: `rad bot` demo zep@lexaloffle
-- author: Al Rado 03.03.2017
-- desc: ported from Pico-8 =)
-- script: lua
-- pal: PICO8
function TIC()
cls()
t=time()/3000
for i=0,1,0.1 do
x=120+cos(i+t)*48
y=68+sin(i+t)*24
radius=y/12
-- divide by 2 for slowed color change
color=12+(y/2)%4
circ(x,y,radius,color)
print("rad bot!",100,65,color)
end
end
-- PICO-8 cos
function cos(a)
return math.cos(2*math.pi*a)
end
-- PICO-8 sin
function sin(a)
return -math.sin(2*math.pi*a)
end
-- title: Trace all global variables
-- author: Al Rado 28.02.2017
-- desc: Standard Lua demo
-- script: lua
-- input: gamepad
-- pal: DB16
local seen={}
function dump(t,i)
seen[t]=true
local s={}
local n=0
for k in pairs(t) do
n=n+1 s[n]=k
end
table.sort(s)
for k,v in ipairs(s) do
trace(i..v)
v=t[v]
if type(v)=="table" and not seen[v] then
dump(v,i.."\t")
end
end
end
cls()
trace("---------------------")
trace("All global variables:")
dump(_G,"")
trace("---------------------")
print("See all global variables in console!")
function TIC() endI wrote a separate static class "Input" on the basis of your ) Very comfortably.
-- script: moon
-- static class, not need instantiate it
class Input
UP=0
DOWN=1
LEFT=2
RIGHT=3
GetHorAxis:=> @btnAxis(LEFT,RIGHT)
GetVerAxis:=> @btnAxis(UP,DOWN)
btnAxis:(a,b)=>
if btn(a) and btn(b)
return 0
elseif btn(a)
return -1
elseif btn(b)
return 1
else
return 0
class Spr
new:(id=0,x=0,y=0,alpha=-1,scale=1,flip=0,rotate=0)=>
@id=id
@x=x
@y=y
@alpha=alpha
@scale=scale
@flip=flip
@rotate=rotate
draw:=>
spr(@id,@x,@y,@alpha,@scale,@flip,@rotate)
--------------------
logo=Spr(1,104,24,2,4)
export TIC=->
--update
logo.x+=Input\GetHorAxis()
logo.y+=Input\GetVerAxis()
logo.id=time()%1000//500+1
--draw
cls(12)
logo\draw()
print("HELLO WORLD!",84,64)
-- title: TIC-80 controller test
-- author: Al Rado 16.02.2017
-- desc: controller test, only moonScript
-- script: moon
-- input: gamepad
-- pal: DB16
TITLE="TIC-80 controller test"
SCREEN_W=240
export class Gamepad
new:(name,startCode=0,mainColor=6,sound=36)=>
@name=name
@buttons={
{name:"Up", code:0+startCode, x:7, y:4},
{name:"Down", code:1+startCode, x:7, y:12},
{name:"Left", code:2+startCode, x:3, y:8},
{name:"Right", code:3+startCode, x:11, y:8},
{name:"Btn A", code:4+startCode, x:23, y:10},
{name:"Btn B", code:5+startCode, x:30, y:6}
}
@pressedKeys={}
@mainColor=mainColor
@bodyColor=7
@shadowColor=1
@pressedBtnColor=5
@sound=sound
Update:(x,y,msgY)=>
@DrawBack(x,y)
@pressedKeys={}
for button in *@buttons
if (btn button.code) then
table.insert @pressedKeys, button.name
circ x+button.x,y+button.y,2,@pressedBtnColor
@PrintKeys msgY
PrintKeys:(y)=>
if (#@pressedKeys > 0) then
keysMsg=""
for i=1, #@pressedKeys
keysMsg..=@pressedKeys[i]
if (i<#@pressedKeys) then keysMsg..=", "
title=@name.." pressed: "
len=print title..keysMsg,-100,-100 --fake print
posX=(SCREEN_W-len)/2
shiftX=print title,posX,y
print keysMsg,posX+shiftX,y,@mainColor
IsActive:=>#@pressedKeys>0
AddViewTo:(views)=>
if (@IsActive()) then table.insert views, ViewParam(@mainColor,@sound)
DrawBack:(x,y)=>
doubleDraw=(drawFunc,colorOne,colorTwo)->
drawFunc 1, colorOne
drawFunc 0, colorTwo
drawBody=(shiftX,shiftY,colorOne,colorTwo)->
drawLayer=(shift,color)->
rect x+shiftX+shift,y+shiftY+shift,36,18,color
doubleDraw(drawLayer,colorOne,colorTwo)
drawDpad=(shiftX,shiftY,colorOne,colorTwo)->
drawLayer=(shift,color)->
rect x+shiftX+shift,y+shiftY+shift,9,3,color
rect x+shiftX+shift+3,y+shiftY+shift-3,3,9,color
doubleDraw(drawLayer,colorOne,colorTwo)
drawButton=(shiftX,shiftY,colorOne,colorTwo)->
drawLayer=(shift,color)->
circ x+shiftX+shift,y+shiftY+shift,2,color
doubleDraw(drawLayer,colorOne,colorTwo)
drawBody(0,0,@shadowColor,@bodyColor)
drawDpad(3,7,@shadowColor,@mainColor)
drawButton(23,10,@shadowColor,@mainColor)
drawButton(30,6,@shadowColor,@mainColor)
export class ViewParam
new:(color,sound)=>
@color=color
@sound=sound
export UpdateSingleView=(view)->
Border view.color
Beep view.sound
export Border=(color)->poke(0x3FF8,color)
export Beep=(sound)->sfx(sound,sound,0,3)
export gmpdOne=Gamepad("Player one")
export gmpdTwo=Gamepad("Player two",8,13,10)
export NOT_ACTIVE=ViewParam(8,-1)
export BOTH_ACTIVE=ViewParam(15,23)
export TIC=->
cls 8
print(TITLE,70,30)
gmpdOne\Update(50,80,116)
gmpdTwo\Update(160,80,126)
views={NOT_ACTIVE}
gmpdOne\AddViewTo(views)
gmpdTwo\AddViewTo(views)
UpdateSingleView #views>2 and BOTH_ACTIVE or views[#views]