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() end
I 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]