First commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
--- @class EventBus
|
||||
EventBus = {
|
||||
_handlers = {},
|
||||
new = function(self, o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
---
|
||||
---@param self EventBus
|
||||
---@param event any
|
||||
---@param handler any
|
||||
---@param index any
|
||||
on = function(self, event, handler, index)
|
||||
if not self._handlers[event] then self._handlers[event] = {} end
|
||||
self:off(event, handler)
|
||||
if not index then
|
||||
table.insert(self._handlers[event], handler)
|
||||
else
|
||||
-- insertIntoTable(this.handlers[event], handler, index)
|
||||
end
|
||||
end,
|
||||
---
|
||||
---@param self EventBus
|
||||
---@param event any
|
||||
---@param handler any
|
||||
off = function(self, event, handler)
|
||||
if not self._handlers[event] then return end
|
||||
self._handlers[event] = table.filter(self._handlers[event], function(o) return o ~= handler end)
|
||||
end,
|
||||
---
|
||||
---@param self EventBus
|
||||
---@param event any
|
||||
---@param ... unknown
|
||||
emit = function(self, event, ...)
|
||||
trace("[EventBus] Emitted " .. event)
|
||||
if not self._handlers[event] then return end
|
||||
for k, handler in pairs(self._handlers[event]) do
|
||||
handler(...)
|
||||
end
|
||||
end
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
-- manage clicks, since tic-80 only returns a "pressed" state
|
||||
|
||||
Input = {
|
||||
--- @type boolean
|
||||
lclick_prev_state = false,
|
||||
--- @type 'none'|'pressed'|'released'
|
||||
lclick_state = "none"
|
||||
}
|
||||
|
||||
function Input.update_mouse(self)
|
||||
local x, y, left = mouse()
|
||||
-- left btn down
|
||||
if left then
|
||||
-- was not pressed before
|
||||
if not self.lclick_prev_state then
|
||||
self.lclick_state = "pressed"
|
||||
self.lclick_prev_state = true
|
||||
|
||||
-- was already pressed
|
||||
elseif self.lclick_prev_state then
|
||||
self.lclick_state = "none"
|
||||
end
|
||||
-- left btn up
|
||||
elseif not left then
|
||||
if self.lclick_prev_state then
|
||||
self.lclick_state = "released"
|
||||
self.lclick_prev_state = false
|
||||
elseif not self.lclick_prev_state then
|
||||
self.lclick_state = "none"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Input.mouse_pressed(self)
|
||||
return self.lclick_state == "pressed"
|
||||
end
|
||||
|
||||
function Input.mouse_released(self)
|
||||
return self.lclick_state == "released"
|
||||
end
|
||||
@@ -0,0 +1,98 @@
|
||||
function draw9box(corner, edge, x, y, width, height)
|
||||
-- edges
|
||||
for i = 8, width - 9, 8 do
|
||||
spr(edge, x + i, y, 0) -- top
|
||||
spr(edge, x + i, y + height - 8, 0, 1, 0, 2) -- bottom
|
||||
end
|
||||
for i = 8, height - 8,8 do
|
||||
spr(edge, x, y + i, 0, 1, 0, 3) -- left
|
||||
spr(edge, x + width - 8, y + i, 0, 1, 0, 1) -- right
|
||||
end
|
||||
-- corners
|
||||
spr(corner, x, y, 0) -- top left
|
||||
spr(corner, x + width - 8, y, 0, 1, 0, 1) -- top right
|
||||
spr(corner, x, y + height - 8, 0, 1, 0, 3) -- bottom left
|
||||
spr(corner, x + width - 8, y + height - 8, 0, 1, 0, 2) -- bottom right
|
||||
end
|
||||
|
||||
local function rot(x, y, rad)
|
||||
local sa = math.sin(rad)
|
||||
local ca = math.cos(rad)
|
||||
return x * ca - y * sa, x * sa + y * ca
|
||||
end
|
||||
|
||||
--- Draw a sprite using two textured triangles.
|
||||
--- Apply affine transformations: scale, shear, rotate, flip
|
||||
--- https://cxong.github.io/tic-80-examples/affine-sprites
|
||||
---comment
|
||||
---@param id number
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param colorkey? number
|
||||
---@param sx? number scale x
|
||||
---@param sy? number scale y
|
||||
---@param flip? number
|
||||
---@param rotate? number
|
||||
---@param w? number
|
||||
---@param h? number
|
||||
---@param ox? number
|
||||
---@param oy? number
|
||||
---@param shx1? number
|
||||
---@param shy1? number
|
||||
---@param shx2? number
|
||||
---@param shy2? number
|
||||
function aspr(
|
||||
id, x, y, colorkey, sx, sy, flip, rotate, w, h,
|
||||
ox, oy, shx1, shy1, shx2, shy2
|
||||
)
|
||||
colorkey = colorkey or -1
|
||||
sx = sx or 1
|
||||
sy = sy or 1
|
||||
flip = flip or 0
|
||||
rotate = math.rad(rotate or 0)
|
||||
w = w or 1
|
||||
h = h or 1
|
||||
ox = ox or w * 8 // 2
|
||||
oy = oy or h * 8 // 2
|
||||
shx1 = shx1 or 0
|
||||
shy1 = shy1 or 0
|
||||
shx2 = shx2 or 0
|
||||
shy2 = shy2 or 0
|
||||
|
||||
-- scale / flip
|
||||
if flip % 2 == 1 then
|
||||
sx = -sx
|
||||
end
|
||||
if flip >= 2 then
|
||||
sy = -sy
|
||||
end
|
||||
ox = ox * -sx
|
||||
oy = oy * -sy
|
||||
|
||||
-- shear / rotate
|
||||
shx1 = shx1 * -sx
|
||||
shy1 = shy1 * -sy
|
||||
shx2 = shx2 * -sx
|
||||
shy2 = shy2 * -sy
|
||||
|
||||
local rx1, ry1 = rot(ox + shx1, oy + shy1, rotate)
|
||||
local rx2, ry2 = rot(((w * 8) * sx) + ox + shx1, oy + shy2, rotate)
|
||||
local rx3, ry3 = rot(ox + shx2, ((h * 8) * sy) + oy + shy1, rotate)
|
||||
local rx4, ry4 = rot(((w * 8) * sx) + ox + shx2, ((h * 8) * sy) + oy + shy2, rotate)
|
||||
local x1 = x + rx1
|
||||
local y1 = y + ry1
|
||||
local x2 = x + rx2
|
||||
local y2 = y + ry2
|
||||
local x3 = x + rx3
|
||||
local y3 = y + ry3
|
||||
local x4 = x + rx4
|
||||
local y4 = y + ry4
|
||||
-- UV coords
|
||||
local u1 = (id % 16) * 8
|
||||
local v1 = id // 16 * 8
|
||||
local u2 = u1 + w * 8
|
||||
local v2 = v1 + h * 8
|
||||
|
||||
ttri(x1, y1, x2, y2, x3, y3, u1, v1, u2, v1, u1, v2, 0, colorkey)
|
||||
ttri(x3, y3, x4, y4, x2, y2, u1, v2, u2, v2, u2, v1, 0, colorkey)
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
--- Finds the first value in a table that satisfies a condition
|
||||
---@param table any
|
||||
---@param cb any @function(v, i) return boolean end
|
||||
---@return unknown @value or nil
|
||||
---@return unknown @index or nil
|
||||
function table.find(table, cb)
|
||||
for i, v in pairs(table) do
|
||||
if cb(v) then
|
||||
return v, i
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
function table.filter(table, cb)
|
||||
local new = {}
|
||||
for _, v in pairs(table) do
|
||||
if cb(v) then
|
||||
table.insert(new, v)
|
||||
end
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
function table.remove_item(list, item)
|
||||
for i, v in pairs(list) do
|
||||
if v == item then
|
||||
table.remove(list, i)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---needle can be a value or a function
|
||||
---@param list table
|
||||
---@param needle function|number
|
||||
---@return integer
|
||||
function table.count(list, needle)
|
||||
local count = 0
|
||||
for _, v in pairs(list) do
|
||||
if type(needle) == "function" and needle(v) or v == needle then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function table.max(list, cb)
|
||||
local max = -math.huge
|
||||
for _, v in pairs(list) do
|
||||
local value = cb(v)
|
||||
if value > max then
|
||||
max = value
|
||||
end
|
||||
end
|
||||
return max
|
||||
end
|
||||
|
||||
function math.randomitem(tbl)
|
||||
return tbl[math.random(#tbl)]
|
||||
end
|
||||
|
||||
function table.random(tbl)
|
||||
return tbl[math.random(#tbl)]
|
||||
end
|
||||
+400
@@ -0,0 +1,400 @@
|
||||
-- https://github.com/kikito/tween.lua
|
||||
|
||||
--[[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
-- Modified by Simon Cambier to better fit TIC-80 restrictions
|
||||
|
||||
---@return Tween
|
||||
function require_tween()
|
||||
-- easing
|
||||
|
||||
-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.
|
||||
-- For all easing functions:
|
||||
-- t = time == how much time has to pass for the tweening to complete
|
||||
-- b = begin == starting property value
|
||||
-- c = change == ending - beginning
|
||||
-- d = duration == running time. How much time has passed *right now*
|
||||
|
||||
---@class Tween
|
||||
local Tween = {}
|
||||
local Tween_mt = { __index = Tween }
|
||||
|
||||
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
|
||||
|
||||
-- linear
|
||||
local function linear(t, b, c, d) return c * t / d + b end
|
||||
|
||||
-- quad
|
||||
local function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end
|
||||
local function outQuad(t, b, c, d)
|
||||
t = t / d
|
||||
return -c * t * (t - 2) + b
|
||||
end
|
||||
local function inOutQuad(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 2) + b end
|
||||
return -c / 2 * ((t - 1) * (t - 3) - 1) + b
|
||||
end
|
||||
local function outInQuad(t, b, c, d)
|
||||
if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end
|
||||
return inQuad((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- cubic
|
||||
local function inCubic(t, b, c, d) return c * pow(t / d, 3) + b end
|
||||
local function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end
|
||||
local function inOutCubic(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * t * t * t + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * t + 2) + b
|
||||
end
|
||||
local function outInCubic(t, b, c, d)
|
||||
if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end
|
||||
return inCubic((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- quart
|
||||
local function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end
|
||||
local function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end
|
||||
local function inOutQuart(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 4) + b end
|
||||
return -c / 2 * (pow(t - 2, 4) - 2) + b
|
||||
end
|
||||
local function outInQuart(t, b, c, d)
|
||||
if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end
|
||||
return inQuart((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- quint
|
||||
local function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end
|
||||
local function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end
|
||||
local function inOutQuint(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 5) + b end
|
||||
return c / 2 * (pow(t - 2, 5) + 2) + b
|
||||
end
|
||||
local function outInQuint(t, b, c, d)
|
||||
if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end
|
||||
return inQuint((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- sine
|
||||
local function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end
|
||||
local function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end
|
||||
local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end
|
||||
local function outInSine(t, b, c, d)
|
||||
if t < d / 2 then return outSine(t * 2, b, c / 2, d) end
|
||||
return inSine((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- expo
|
||||
local function inExpo(t, b, c, d)
|
||||
if t == 0 then return b end
|
||||
return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
|
||||
end
|
||||
local function outExpo(t, b, c, d)
|
||||
if t == d then return b + c end
|
||||
return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
|
||||
end
|
||||
local function inOutExpo(t, b, c, d)
|
||||
if t == 0 then return b end
|
||||
if t == d then return b + c end
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end
|
||||
return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
|
||||
end
|
||||
local function outInExpo(t, b, c, d)
|
||||
if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end
|
||||
return inExpo((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- circ
|
||||
local function inCirc(t, b, c, d) return (-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end
|
||||
local function outCirc(t, b, c, d) return (c * sqrt(1 - pow(t / d - 1, 2)) + b) end
|
||||
local function inOutCirc(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (sqrt(1 - t * t) + 1) + b
|
||||
end
|
||||
local function outInCirc(t, b, c, d)
|
||||
if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end
|
||||
return inCirc((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- elastic
|
||||
local function calculatePAS(p, a, c, d)
|
||||
p, a = p or d * 0.3, a or 0
|
||||
if a < abs(c) then return p, c, p / 4 end -- p, a, s
|
||||
return p, a, p / (2 * pi) * asin(c / a) -- p,a,s
|
||||
end
|
||||
local function inElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p, a, s = calculatePAS(p, a, c, d)
|
||||
t = t - 1
|
||||
return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
|
||||
end
|
||||
local function outElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p, a, s = calculatePAS(p, a, c, d)
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
|
||||
end
|
||||
local function inOutElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d * 2
|
||||
if t == 2 then return b + c end
|
||||
p, a, s = calculatePAS(p, a, c, d)
|
||||
t = t - 1
|
||||
if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) * 0.5 + c + b
|
||||
end
|
||||
local function outInElastic(t, b, c, d, a, p)
|
||||
if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end
|
||||
return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
|
||||
end
|
||||
|
||||
-- back
|
||||
local function inBack(t, b, c, d, s)
|
||||
s = s or 1.70158
|
||||
t = t / d
|
||||
return c * t * t * ((s + 1) * t - s) + b
|
||||
end
|
||||
local function outBack(t, b, c, d, s)
|
||||
s = s or 1.70158
|
||||
t = t / d - 1
|
||||
return c * (t * t * ((s + 1) * t + s) + 1) + b
|
||||
end
|
||||
local function inOutBack(t, b, c, d, s)
|
||||
s = (s or 1.70158) * 1.525
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
|
||||
end
|
||||
local function outInBack(t, b, c, d, s)
|
||||
if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end
|
||||
return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
|
||||
end
|
||||
|
||||
-- bounce
|
||||
local function outBounce(t, b, c, d)
|
||||
t = t / d
|
||||
if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end
|
||||
if t < 2 / 2.75 then
|
||||
t = t - (1.5 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.75) + b
|
||||
elseif t < 2.5 / 2.75 then
|
||||
t = t - (2.25 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.9375) + b
|
||||
end
|
||||
t = t - (2.625 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.984375) + b
|
||||
end
|
||||
local function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end
|
||||
local function inOutBounce(t, b, c, d)
|
||||
if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end
|
||||
return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b
|
||||
end
|
||||
local function outInBounce(t, b, c, d)
|
||||
if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end
|
||||
return inBounce((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
Tween.easing = {
|
||||
linear = linear,
|
||||
inQuad = inQuad,
|
||||
outQuad = outQuad,
|
||||
inOutQuad = inOutQuad,
|
||||
outInQuad = outInQuad,
|
||||
inCubic = inCubic,
|
||||
outCubic = outCubic,
|
||||
inOutCubic = inOutCubic,
|
||||
outInCubic = outInCubic,
|
||||
inQuart = inQuart,
|
||||
outQuart = outQuart,
|
||||
inOutQuart = inOutQuart,
|
||||
outInQuart = outInQuart,
|
||||
inQuint = inQuint,
|
||||
outQuint = outQuint,
|
||||
inOutQuint = inOutQuint,
|
||||
outInQuint = outInQuint,
|
||||
inSine = inSine,
|
||||
outSine = outSine,
|
||||
inOutSine = inOutSine,
|
||||
outInSine = outInSine,
|
||||
inExpo = inExpo,
|
||||
outExpo = outExpo,
|
||||
inOutExpo = inOutExpo,
|
||||
outInExpo = outInExpo,
|
||||
inCirc = inCirc,
|
||||
outCirc = outCirc,
|
||||
inOutCirc = inOutCirc,
|
||||
outInCirc = outInCirc,
|
||||
inElastic = inElastic,
|
||||
outElastic = outElastic,
|
||||
inOutElastic = inOutElastic,
|
||||
outInElastic = outInElastic,
|
||||
inBack = inBack,
|
||||
outBack = outBack,
|
||||
inOutBack = inOutBack,
|
||||
outInBack = outInBack,
|
||||
inBounce = inBounce,
|
||||
outBounce = outBounce,
|
||||
inOutBounce = inOutBounce,
|
||||
outInBounce = outInBounce
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- private stuff
|
||||
|
||||
local function copyTables(destination, keysTable, valuesTable)
|
||||
valuesTable = valuesTable or keysTable
|
||||
local mt = getmetatable(keysTable)
|
||||
if mt and getmetatable(destination) == nil then
|
||||
setmetatable(destination, mt)
|
||||
end
|
||||
for k, v in pairs(keysTable) do
|
||||
if type(v) == "table" then
|
||||
destination[k] = copyTables({}, v, valuesTable[k])
|
||||
else
|
||||
destination[k] = valuesTable[k]
|
||||
end
|
||||
end
|
||||
return destination
|
||||
end
|
||||
|
||||
local function checkSubjectAndTargetRecursively(subject, target, path)
|
||||
path = path or {}
|
||||
local targetType, newPath
|
||||
for k, targetValue in pairs(target) do
|
||||
targetType, newPath = type(targetValue), copyTables({}, path)
|
||||
table.insert(newPath, tostring(k))
|
||||
if targetType == "number" then
|
||||
assert(type(subject[k]) == "number",
|
||||
"Parameter '" .. table.concat(newPath, "/") .. "' is missing from subject or isn't a number")
|
||||
elseif targetType == "table" then
|
||||
checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
|
||||
else
|
||||
assert(targetType == "number",
|
||||
"Parameter '" .. table.concat(newPath, "/") .. "' must be a number or table of numbers")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkNewParams(duration, subject, target, easing)
|
||||
assert(type(duration) == "number" and duration > 0,
|
||||
"duration must be a positive number. Was " .. tostring(duration))
|
||||
local tsubject = type(subject)
|
||||
assert(tsubject == "table" or tsubject == "userdata",
|
||||
"subject must be a table or userdata. Was " .. tostring(subject))
|
||||
assert(type(target) == "table", "target must be a table. Was " .. tostring(target))
|
||||
assert(type(easing) == "function", "easing must be a function. Was " .. tostring(easing))
|
||||
checkSubjectAndTargetRecursively(subject, target)
|
||||
end
|
||||
|
||||
local function getEasingFunction(easing)
|
||||
easing = easing or "linear"
|
||||
if type(easing) == "string" then
|
||||
local name = easing
|
||||
easing = Tween.easing[name]
|
||||
if type(easing) ~= "function" then
|
||||
error("The easing function name '" .. name .. "' is invalid")
|
||||
end
|
||||
end
|
||||
return easing
|
||||
end
|
||||
|
||||
local function performEasingOnSubject(subject, target, initial, clock, duration, easing)
|
||||
local t, b, c, d
|
||||
for k, v in pairs(target) do
|
||||
if type(v) == "table" then
|
||||
performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
|
||||
else
|
||||
t, b, c, d = clock, initial[k], v - initial[k], duration
|
||||
subject[k] = easing(t, b, c, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Tween methods
|
||||
|
||||
---@param clock number
|
||||
---@return boolean # true if the tween has expired
|
||||
function Tween:set(clock)
|
||||
assert(type(clock) == "number", "clock must be a positive number or 0")
|
||||
|
||||
self.initial = self.initial or copyTables({}, self.target, self.subject)
|
||||
self.clock = clock
|
||||
|
||||
if self.clock <= 0 then
|
||||
self.clock = 0
|
||||
copyTables(self.subject, self.initial)
|
||||
elseif self.clock >= self.duration then -- the tween has expired
|
||||
self.clock = self.duration
|
||||
copyTables(self.subject, self.target)
|
||||
else
|
||||
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
|
||||
end
|
||||
|
||||
return self.clock >= self.duration
|
||||
end
|
||||
|
||||
function Tween:reset()
|
||||
return self:set(0)
|
||||
end
|
||||
|
||||
---@param dt number
|
||||
---@return boolean # true if the tween has expired
|
||||
function Tween:update(dt)
|
||||
assert(type(dt) == "number", "dt must be a number")
|
||||
return self:set(self.clock + dt)
|
||||
end
|
||||
|
||||
-- Public interface
|
||||
|
||||
function Tween.new(duration, subject, target, easing)
|
||||
easing = getEasingFunction(easing)
|
||||
checkNewParams(duration, subject, target, easing)
|
||||
return setmetatable({
|
||||
duration = duration,
|
||||
subject = subject,
|
||||
target = target,
|
||||
easing = easing,
|
||||
clock = 0
|
||||
}, Tween_mt)
|
||||
end
|
||||
|
||||
return Tween
|
||||
end
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
include "globals"
|
||||
include "utils.tables"
|
||||
include "utils.rendering"
|
||||
|
||||
--- Print with outline
|
||||
function print_border(text, x, y, color, outline, fixed, scale, smallfont)
|
||||
local outline = outline == nil
|
||||
and ((color == 0 or color == nil) and 12 or 0)
|
||||
or outline
|
||||
-- diagonals
|
||||
print(text, x + 1, y + 1, outline, fixed, scale, smallfont)
|
||||
print(text, x + 1, y - 1, outline, fixed, scale, smallfont)
|
||||
print(text, x - 1, y + 1, outline, fixed, scale, smallfont)
|
||||
print(text, x - 1, y - 1, outline, fixed, scale, smallfont)
|
||||
-- cardinals
|
||||
print(text, x + 1, y, outline, fixed, scale, smallfont)
|
||||
print(text, x - 1, y, outline, fixed, scale, smallfont)
|
||||
print(text, x, y + 1, outline, fixed, scale, smallfont)
|
||||
print(text, x, y - 1, outline, fixed, scale, smallfont)
|
||||
|
||||
print(text, x, y, color, fixed, scale, smallfont)
|
||||
end
|
||||
|
||||
--- Print debug
|
||||
-- function printd(text, x, y)
|
||||
-- print_border(text, x, y, 12, 2)
|
||||
-- end
|
||||
|
||||
--- Like print(), but with font() and alignment
|
||||
---@param txt string
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param color number
|
||||
---@param align? number
|
||||
---@param scale? number
|
||||
---@return number
|
||||
function prn(txt, x, y, color, align, scale)
|
||||
align = align or ALIGN.Left
|
||||
scale = scale or 1
|
||||
set1bpp()
|
||||
if align == ALIGN.Right then
|
||||
x = x - prn_len(txt, scale)
|
||||
elseif align == ALIGN.Center then
|
||||
x = x - prn_len(txt, scale) / 2
|
||||
end
|
||||
if color ~= nil then
|
||||
swap_color(1, color)
|
||||
end
|
||||
local len = font(txt, x, y, 0, 4, 0, false, scale)
|
||||
swap_color(1, 1)
|
||||
set4bpp()
|
||||
return len
|
||||
end
|
||||
|
||||
---Like prn() but with an outline
|
||||
---@param any string
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param color number
|
||||
---@param align? number
|
||||
---@param border_color? number
|
||||
---@param scale? number
|
||||
---@return number
|
||||
function prn_border(txt, x, y, color, align, border_color, scale)
|
||||
if not border_color then
|
||||
return prn(txt, x, y, color, align, scale)
|
||||
end
|
||||
align = align or ALIGN.Left
|
||||
border_color = border_color or 0
|
||||
scale = scale or 1
|
||||
-- diagonals
|
||||
prn(txt, x + 1, y + 1, border_color, align, scale)
|
||||
prn(txt, x + 1, y - 1, border_color, align, scale)
|
||||
prn(txt, x - 1, y + 1, border_color, align, scale)
|
||||
prn(txt, x - 1, y - 1, border_color, align, scale)
|
||||
-- cardinals
|
||||
prn(txt, x + 1, y, border_color, align, scale)
|
||||
prn(txt, x - 1, y, border_color, align, scale)
|
||||
prn(txt, x, y + 1, border_color, align, scale)
|
||||
prn(txt, x, y - 1, border_color, align, scale)
|
||||
|
||||
return prn(txt, x, y, color, align, scale)
|
||||
end
|
||||
|
||||
---like prn_border() but with floaty text
|
||||
---@param txt any
|
||||
---@param x any
|
||||
---@param y any
|
||||
---@param color any
|
||||
---@param align any
|
||||
---@param border_color? any
|
||||
---@param scale? any
|
||||
function prn_border_floaty(txt, x, y, color, align, border_color, scale, delay)
|
||||
delay = delay or 0
|
||||
align = align or ALIGN.Left
|
||||
border_color = border_color or 0
|
||||
local len = prn_len(txt, scale)
|
||||
prn_len(txt, scale)
|
||||
if align == ALIGN.Right then
|
||||
x = x - len
|
||||
elseif align == ALIGN.Center then
|
||||
x = x - len / 2
|
||||
end
|
||||
for i = 1, #txt do
|
||||
local c = txt:sub(i, i)
|
||||
local y = y + math.sin(((i+delay) * 100 + time()) / 200) * 3
|
||||
x = x + prn_border(c, x, y, color, ALIGN.Left, border_color, scale)
|
||||
end
|
||||
return len
|
||||
end
|
||||
|
||||
function swap_color(index, color)
|
||||
poke4(PALETTE_MAP * 2 + index, color)
|
||||
end
|
||||
|
||||
function reset_colors(...)
|
||||
args = { ... }
|
||||
if #args > 0 then
|
||||
for _, c in ipairs(args) do
|
||||
swap_color(c, c)
|
||||
end
|
||||
else
|
||||
for i = 0, 16 do
|
||||
poke4(PALETTE_MAP * 2 + i, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _cachedPrintLen = {}
|
||||
function print_len(txt)
|
||||
if _cachedPrintLen[txt] then return _cachedPrintLen[txt] end
|
||||
local len = print(txt, -1000, -1000)
|
||||
_cachedPrintLen[txt] = len
|
||||
return len
|
||||
end
|
||||
|
||||
local _cachedFontLen = {}
|
||||
function prn_len(txt, scale)
|
||||
scale = scale or 1
|
||||
if scale == 0 then scale = 1 end
|
||||
local bpp = peek4(2 * 0x3ffc) -- get the current bpp value to reset it after
|
||||
set1bpp()
|
||||
if _cachedFontLen[txt] then return _cachedFontLen[txt] end
|
||||
local len = font(txt, -1000, -1000, 0, 4, 0, false, scale)
|
||||
_cachedFontLen[txt] = len
|
||||
poke4(2 * 0x3ffc, bpp)
|
||||
return len
|
||||
end
|
||||
|
||||
function set1bpp()
|
||||
poke4(2 * 0x3ffc, 8) -- 0b1000
|
||||
end
|
||||
|
||||
function set4bpp()
|
||||
poke4(2 * 0x3ffc, 2) -- 0b0010
|
||||
end
|
||||
|
||||
function point_in_rect(x, y, rx, ry, rw, rh)
|
||||
return x >= rx and x <= rx + rw and y >= ry and y <= ry + rh
|
||||
end
|
||||
|
||||
function mouse_in_rect(rect)
|
||||
local mx, my = mouse()
|
||||
return point_in_rect(mx, my, rect.x, rect.y, rect.width, rect.height)
|
||||
end
|
||||
|
||||
function clamp(min, val, max)
|
||||
return math.max(math.min(max, val), min)
|
||||
end
|
||||
|
||||
function normalize(val, min, max)
|
||||
min = min or 0
|
||||
max = max or val
|
||||
return (val - min) / (max - min)
|
||||
end
|
||||
Reference in New Issue
Block a user