Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

alrado

55
Posts
13
Topics
9
Followers
1
Following
A member registered Jan 30, 2017 · View creator page →

Creator of

Recent community posts

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 )

Thanks! Great references and graphics from pixel-artists.

Thanks!

In every game there are bugs and features =) If bugs make the game better - then these are features ))

Hello!

This is not a game, but a very small intro - tweetcart.
It's a program that fits into the Twitter message limit (about 280 characters).

I read and wrote there )

Maybe we evaluate the quality in different ways?

"RaspberryPi 3B -> 16 FPS, and in the browser on the laptop -> 13 FPS" is it really bad FPS? 
But if Nesbox succeeds in making TIC-80 faster, I'll be glad!

On my Raspberry Pi 3B (RetroPie)  TIC-80 run very well - 16FPS in cartridge perfomance_monitor, in other games FPS  much higher - more than 30

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

I did not find a problem here )

-- script: lua
function build_obj()
  local z={attribute1="blah",
           attribute2="blah",
           attribute3="spam"}
 return z
end
obj1=build_obj()
if obj1.attribute1=="blah" then
  trace("do awesome stuff")
end
function TIC()
    exit()
end

At the moment there is no such functionality.

Whether to find out whether at least one button is pressed:

if (btn() ~= 0) then 
  -- True if any button is pressed
end

Obviously the author of the script would have coped with this better than anyone else )

(2 edits)

I tried to transfer to TIC-80 ) WIP

When everything works out, please show the code, I'm interested in how to save the numbers in the text.

"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.

ADDR - is address FG(BG addres + 256 sprites)
It may be different, at your discretion.
I agree, not quite correctly written, it will be more accurate:

SPRITE_ADDR=0x4000
BYTES_IN_SPR=32
SPR_IX=256
ADDR=(SPRITE_ADDR+BYTES_IN_SPR*SPR_IX)*2

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

I'll write how I'll be free

You can use LZW-compressor for graphics and save a lot of space, here is an example https://tic.computer/play?cart=86

`poke4` writes nibble (half of byte, 4 bites) = pixel

`poke` writes a byte = 2 pixel

"But still the same sprite (TIC-80) logo is displayed in index 5" - there is written the contents of the string sprDataHex.
Clear the area of the sprite sheet and run the cartridge - you will see this

https://github.com/AlRado/Sublime-TIC-80

Guys, thank you for the demos!

P.S. All this demos used on gitBook

Unfortunately I was able to issue them only in Russian, I have poor English

Lua collision-detection library for axis-aligned rectangles. Its main features are:

  • bump.lua only does axis-aligned bounding-box (AABB) collisions.
  • Handles tunnelling - all items are treated as "bullets". The fact that we only use AABBs allows doing this fast.
  • Strives to be fast while being economic in memory
  • It's centered on detection, but it also offers some (minimal & basic) collision response
  • Can also return the items that touch a point, a segment or a rectangular zone.
  • bump.lua is gameistic instead of realistic.

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"

Thanks, i took yours re-ordered CGA palette.

Thanks, added.

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

I like it! =)

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

Of course, unnecessary features can be removed, right? )

'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

Thanks! Sounds great, I can try )

-- 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)

Very interesting, thank you!

-- 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]