split uranium-core into its own repository

This commit is contained in:
Jill 2022-09-19 19:22:47 +03:00
parent 36ee8fd056
commit f14f71a846
17 changed files with 6 additions and 2314 deletions

View File

@ -1 +1,2 @@
pull.rebase=true
pull.rebase=true
submodule.recurse=true

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "template"]
path = template
url = https://git.oat.zone/oat/uranium-core

1
template Submodule

@ -0,0 +1 @@
Subproject commit 0e8905d660d4104886dc559eba8b41ce8901b6fc

View File

@ -1,6 +0,0 @@
<ActorFrame InitCommand="%oat._actor.initFrame">
<children>
<Layer Condition="oat._actor.cond()" Type="@oat._actor.type()" File="@oat._actor.file()" Font="@oat._actor.font()" InitCommand="%oat._actor.init"/>
<Layer Condition="oat._actor.next()" File="actors.xml" />
</children>
</ActorFrame>

View File

@ -1,528 +0,0 @@
<Layer Type="ActorFrame" InitCommand="%function(self)
_G.oat = {}
oat._main = self
setmetatable(oat, {
-- if something isn't found in the table, fall back to a global lookup
__index = _G,
-- handle oat() calls to set the environment
__call = function(self, f)
setfenv(f or 2, self)
return f
end
})
oat()
local function copy(src)
local dest = {}
for k, v in pairs(src) do
dest[k] = v
end
return dest
end
oat.oat = _G.oat
oat.type = _G.type
oat.print = _G.print
oat.pairs = _G.pairs
oat.ipairs = _G.ipairs
oat.unpack = _G.unpack
oat.tonumber = _G.tonumber
oat.tostring = _G.tostring
oat.math = copy(_G.math)
oat.table = copy(_G.table)
oat.string = copy(_G.string)
oat.scx = SCREEN_CENTER_X
oat.scy = SCREEN_CENTER_Y
oat.sw = SCREEN_WIDTH
oat.sh = SCREEN_HEIGHT
oat.dw = DISPLAY:GetDisplayWidth()
oat.dh = DISPLAY:GetDisplayHeight()
local uraniumFunc = {}
function uraniumFunc:call(event, ...)
if self._callbacks[event] then
for _, callback in ipairs(self._callbacks[event]) do
callback(unpack(arg))
end
end
end
local uraniumMeta = {}
function uraniumMeta:__newindex(key, value)
if self._callbacks[key] then
table.insert(self._callbacks[key], value)
else
self._callbacks[key] = {value}
end
end
uraniumMeta.__index = uraniumFunc
uranium = setmetatable({_callbacks = {}}, uraniumMeta)
function backToSongWheel(message)
if message then
SCREENMAN:SystemMessage(message)
print(message)
end
GAMESTATE:FinishSong()
-- disable update_command
self:hidden(1)
end
local actorsInitialized = false -- if true, no new actors can be created
local luaobj
local globalQueue = {} -- for resetting
local patchedFunctions = {}
local function patchFunction(f, obj)
if not patchedFunctions[f] then patchedFunctions[f] = {} end
if not patchedFunctions[f][obj] then
patchedFunctions[f][obj] = function(...)
arg[1] = obj
local results
local status, result = pcall(function()
-- doing it this way instead of returning because lua
-- offers no way of grabbing everything BUT the first
-- argument out of pcall
results = {f(unpack(arg))}
end)
if not status then
error(result, 2)
else
return unpack(results)
end
end
end
return patchedFunctions[f][obj]
end
local function onCommand(self)
actorsInitialized = true
uranium:call('init')
end
function reset(actor)
if not actorsInitialized then error('uranium: cannot reset an actor during initialization', 2) end
for _, q in ipairs(globalQueue) do
local queueActor = q[1]
if queueActor == actor.__raw then
local v = q[2]
local func = queueActor[v[1]]
if not func then
-- uhmmm ??? hm. what do we do??
else
patchFunction(func, queueActor)(unpack(v[2]))
end
end
end
end
-- runs once during ScreenReadyCommand, before the user code is loaded
-- hides various actors that are placed by the theme
local function hideThemeActors()
for _, element in ipairs {
'Overlay', 'Underlay',
'ScoreP1', 'ScoreP2',
'LifeP1', 'LifeP2',
} do
local child = SCREENMAN(element)
if child then child:hidden(1) end
end
end
local lastt = GAMESTATE:GetSongTime()
local function screen_ready_command(self)
hideThemeActors()
self:hidden(0)
local errored = false
local firstrun = true
self:addcommand('Update', function()
if errored then
return 0
end
errored = true
t = os.clock()
b = GAMESTATE:GetSongBeat()
local dt = t - lastt
lastt = t
if firstrun then
firstrun = false
dt = 0
self:GetChildren()[2]:hidden(1)
end
for _, q in ipairs(globalQueue) do
local actor = q[1]
local v = q[2]
local func = actor[v[1]]
if not func then
-- uhmmm ??? hm. what do we do??
else
patchFunction(func, actor)(unpack(v[2]))
end
end
uranium:call('update', dt)
errored = false
return 0
end)
self:luaeffect('Update')
end
GAMESTATE:ApplyModifiers('clearall')
local function formatError(file, e)
local _, _, err = string.find(e, 'error loading package `[^`\']+\' %((.+)%)')
return (file and err) and ('error loading \'' .. file .. '\':\n' .. err) or e
end
local songName = GAMESTATE:GetCurrentSong():GetSongDir()
local additionalSongFolders = PREFSMAN:GetPreference('AdditionalSongFolders')
local additionalFolders = PREFSMAN:GetPreference('AdditionalFolders')
local function attemptload(f, filename) -- not to be confused with tryload
local err = ''
local lasterr = ''
local func
local s = '.' .. songName .. f
func, lasterr = loadfile(s)
if func then return func end
if not string.find(lasterr, '\' from path `') then return nil, formatError(filename, lasterr), nil end
err = err .. s .. '\n'
s = f
func, lasterr = loadfile(s)
if func then return func end
if not string.find(lasterr, '\' from path `') then return nil, formatError(filename, lasterr), nil end
err = err .. s .. '\n'
-- cut off 'Songs/' from the path
local _,index = string.find(songName,'Songs/')
local songLoc = string.sub(songName,index)
-- for every songfolder in the additionalsongfolders
if additionalSongFolders and additionalSongFolders ~= '' then
for songFolder in string.gfind(additionalSongFolders,'[^,]+') do
s = songFolder .. songLoc .. f
func, lasterr = loadfile(s)
if func then return func end
err = err .. s .. '\n'
end
end
if additionalFolders and additionalFolders ~= '' then
for folder in string.gfind(additionalFolders,'[^,]+') do
s = folder .. songName .. f
func, lasterr = loadfile(s)
if func then return func end
err = err .. s .. '\n'
end
end
return nil, lasterr, string.sub(err, 0, -2)
end
oat._loadPath = ''
function tryload(f)
local func, err, trace = attemptload(oat._loadPath .. f, f)
if not func then
if trace then
backToSongWheel('finding \'' .. f .. '\' failed, check log for more details')
error('uranium: finding \'' .. f .. '\' failed! tried these paths: ' .. '\n' .. trace, 2)
Trace(trace)
else
error(err, 2)
Trace('loading \'' .. f .. '\' failed!' .. '\n' .. err)
end
else
return oat(func)
end
end
local function getFolderStructure(path)
local folders = {}
for folder in string.gfind(path, '[^/]+') do
table.insert(folders, folder)
end
table.remove(folders, #folders)
return table.concat(folders, '/')
end
oat._requirePath = ''
function require(f)
-- . -> /
f = string.gsub(f, '%.', '/')
-- add .lua
f = f .. '.lua'
local oldpath = oat._requirePath
local folder = getFolderStructure(f)
if folder ~= '' then
oat._requirePath = oat._requirePath .. folder .. '/'
end
local res, c = pcall(tryload, oldpath .. f)
if not res then
error(c, 2)
else
local success, s = pcall(c)
if success then
return s
else
error('uranium: error loading \'' .. f .. '\':\n' .. s, 2)
end
end
oat._requirePath = oldpath
end
-- actors
local actorQueue = {}
local currentActor = nil
oat._actor = {}
function oat._actor.next()
local actor = actorQueue[1]
if actor then
table.remove(actorQueue, 1)
currentActor = actor
return true
else
return false
end
end
function oat._actor.cond()
return currentActor ~= nil
end
function oat._actor.type()
return currentActor.type
end
function oat._actor.file()
return currentActor.file
end
function oat._actor.font()
return currentActor.font
end
function oat._actor.init(self)
currentActor.init(self)
end
function oat._actor.initFrame(self)
local nextChild = self(2)
self:SetDrawFunction(function()
if nextChild then
nextChild:Draw()
end
end)
end
local function createProxyActor(name)
local queue = {}
local initCommands = {}
local lockedActor
return setmetatable({}, {
__index = function(self, key)
if key == '__raw' then
return lockedActor
end
if lockedActor then
local val = lockedActor[key]
if type(val) == 'function' then
return patchFunction(val, lockedActor)
end
return val
end
if key == '__lock' then
return function(actor)
for _, v in ipairs(queue) do
local func = actor[v[1]]
if not func then
error(
'uranium: error on \'' .. name .. '\' initialization on ' .. v[3].short_src .. ':' .. v[3].currentline .. ':\n' ..
'you\'re calling a function \'' .. v[1] .. '\' on a ' .. name .. ' which doesn\'t exist!:\n'
)
else
local success, result = pcall(function()
patchFunction(func, actor)(unpack(v[2]))
end)
if not success then
error(
'uranium: error on \'' .. name .. '\' initialization on ' .. v[3].short_src .. ':' .. v[3].currentline .. ':\n' ..
result
)
end
end
end
-- now that we know there's no poisonous methods in queue, let's offload them
for _, v in ipairs(queue) do
table.insert(globalQueue, {actor, v})
end
-- let's also properly route everything from the proxied actor to the actual actor
lockedActor = actor
-- and now let's run the initcommands
for _, c in ipairs(initCommands) do
local func = c[1]
local success, result = pcall(function()
func(actor)
end)
if not success then
error(
'uranium: error on \'' .. name .. '\' InitCommand defined on ' .. v[3].short_src .. ':' .. v[3].currentline .. ':\n' ..
result
)
end
end
initCommands = {}
end
else
return function(...)
if key == 'addcommand' and arg[2] == 'Init' then
table.insert(initCommands, {arg[3], debug.getinfo(2, 'Sl')})
else
table.insert(queue, {key, arg, debug.getinfo(2, 'Sl')})
end
end
end
end,
__newindex = function()
error('uranium: cannot set properties on actors!', 2)
end,
__tostring = function() return name end,
__name = name
})
end
local function createGenericFunc(type)
return function()
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
local actor = createProxyActor(type)
table.insert(actorQueue, {
type = type,
file = nil,
init = function(a)
actor.__lock(a)
end
})
return actor
end
end
Quad = createGenericFunc('Quad')
ActorProxy = createGenericFunc('ActorProxy')
Polygon = createGenericFunc('Polygon')
function Sprite(file)
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
if not file then error('uranium: cannot create a Sprite without a file', 2) end
local actor = createProxyActor('Sprite')
table.insert(actorQueue, {
type = nil,
file = '../src/' .. oat._requirePath .. file,
init = function(a)
actor.__lock(a)
end
})
return actor
end
function Model(file)
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
if not file then error('uranium: cannot create a Model without a file', 2) end
local actor = createProxyActor('Model')
table.insert(actorQueue, {
type = nil,
file = '../src/' .. oat._requirePath .. file,
init = function(a)
actor.__lock(a)
end
})
return actor
end
function BitmapText(font, text)
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
local actor = createProxyActor('BitmapText')
table.insert(actorQueue, {
type = 'BitmapText',
font = font and ('../src/' .. oat._requirePath .. font) or 'common',
init = function(a)
if text then a:settext(text) end
actor.__lock(a)
end
})
return actor
end
function ActorSound(file)
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
if not file then error('uranium: cannot create an ActorSound without a file', 2) end
local actor = createProxyActor('ActorSound')
table.insert(actorQueue, {
type = 'ActorSound',
file = '../src/' .. oat._requirePath .. file,
init = function(a)
actor.__lock(a)
end
})
return actor
end
oat._loadPath = 'template/stdlib/'
oat.tryload('index')()
oat._loadPath = 'src/'
lua = oat.tryload('main')
if lua then
local success, result = pcall(lua)
if success then
luaobj = result
self:addcommand('On', onCommand)
self:addcommand('Ready', screen_ready_command)
self:queuecommand('Ready')
else
Trace('got an error loading main.lua!')
Trace(result)
backToSongWheel('loading .lua file failed, check log for details')
error('uranium: loading main.lua file failed:\n' .. result)
end
else
Trace('luaobj doesnt exist despite passing checks. thats VERY odd')
backToSongWheel('uranium: loading .lua file failed, check log for details')
error('loading main.lua file failed: \'lua\' variable doesn\'t exist despite passing checks')
end
-- NotITG and OpenITG have a long standing bug where the InitCommand on an actor can run twice in certain cases.
-- By removing the command after it's done, it can only ever run once
self:removecommand('Init')
end"><children>
<Layer Condition="oat._actor.next()" File="actors.xml"/>
<Layer Type="Quad" InitCommand="xywh,SCREEN_CENTER_X,SCREEN_CENTER_Y,SCREEN_WIDTH,SCREEN_HEIGHT;diffuse,#000000;sleep,9e9"/>
</children></Layer>

View File

@ -1,343 +0,0 @@
local M = {_TYPE='module', _NAME='bitop.funcs', _VERSION='1.0-0'}
local floor = math.floor
local MOD = math.pow(2, 32)
local MODM = MOD-1
local function memoize(f)
local mt = {}
local t = setmetatable({}, mt)
function mt:__index(k)
local v = f(k)
t[k] = v
return v
end
return t
end
local function make_bitop_uncached(t, m)
local function bitop(a, b)
local res,p = 0,1
while a ~= 0 and b ~= 0 do
local am, bm = a%m, b%m
res = res + t[am][bm]*p
a = (a - am) / m
b = (b - bm) / m
p = p*m
end
res = res + (a+b) * p
return res
end
return bitop
end
local function make_bitop(t)
local op1 = make_bitop_uncached(t, math.pow(2, 1))
local op2 = memoize(function(a)
return memoize(function(b)
return op1(a, b)
end)
end)
return make_bitop_uncached(op2, math.pow(2, (t.n or 1)))
end
-- ok? probably not if running on a 32-bit int Lua number type platform
function M.tobit(x)
return x % math.pow(2, 32)
end
M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
local bxor = M.bxor
function M.bnot(a) return MODM - a end
local bnot = M.bnot
function M.band(a,b) return ((a+b) - bxor(a,b))/2 end
local band = M.band
function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end
local bor = M.bor
local lshift, rshift -- forward declare
function M.rshift(a,disp) -- Lua5.2 insipred
if disp < 0 then return lshift(a,-disp) end
return floor(a % math.pow(2, 32) / math.pow(2, disp))
end
rshift = M.rshift
function M.lshift(a,disp) -- Lua5.2 inspired
if disp < 0 then return rshift(a,-disp) end
return (a * math.pow(2, disp)) % math.pow(2, 32)
end
lshift = M.lshift
function M.tohex(x, n) -- BitOp style
n = n or 8
local up
if n <= 0 then
if n == 0 then return '' end
up = true
n = - n
end
x = band(x, math.pow(16, n-1))
return ('%0'..n..(up and 'X' or 'x')):format(x)
end
local tohex = M.tohex
function M.extract(n, field, width) -- Lua5.2 inspired
width = width or 1
return band(rshift(n, field), math.pow(2, width-1))
end
local extract = M.extract
function M.replace(n, v, field, width) -- Lua5.2 inspired
width = width or 1
local mask1 = math.pow(2, width-1)
v = band(v, mask1) -- required by spec?
local mask = bnot(lshift(mask1, field))
return band(n, mask) + lshift(v, field)
end
local replace = M.replace
function M.bswap(x) -- BitOp style
local a = band(x, 0xff); x = rshift(x, 8)
local b = band(x, 0xff); x = rshift(x, 8)
local c = band(x, 0xff); x = rshift(x, 8)
local d = band(x, 0xff)
return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
end
local bswap = M.bswap
function M.rrotate(x, disp) -- Lua5.2 inspired
disp = disp % 32
local low = band(x, math.pow(2, disp-1))
return rshift(x, disp) + lshift(low, 32-disp)
end
local rrotate = M.rrotate
function M.lrotate(x, disp) -- Lua5.2 inspired
return rrotate(x, -disp)
end
local lrotate = M.lrotate
M.rol = M.lrotate -- LuaOp inspired
M.ror = M.rrotate -- LuaOp insipred
function M.arshift(x, disp) -- Lua5.2 inspired
local z = rshift(x, disp)
if x >= 0x80000000 then z = z + lshift(math.pow(2, disp-1), 32-disp) end
return z
end
local arshift = M.arshift
function M.btest(x, y) -- Lua5.2 inspired
return band(x, y) ~= 0
end
--
-- Start Lua 5.2 "bit32" compat section.
--
M.bit32 = {} -- Lua 5.2 'bit32' compatibility
local function bit32_bnot(x)
return (-1 - x) % MOD
end
M.bit32.bnot = bit32_bnot
-- something here causes a syntax error so im just commenting out since i dont need it anyways
--[[
local function bit32_bxor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = bxor(a, b)
if c then
z = bit32_bxor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bxor = bit32_bxor
local function bit32_band(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = ((a+b) - bxor(a,b)) / 2
if c then
z = bit32_band(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return MODM
end
end
M.bit32.band = bit32_band
local function bit32_bor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = MODM - band(MODM - a, MODM - b)
if c then
z = bit32_bor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bor = bit32_bor
function M.bit32.btest(...)
return bit32_band(...) ~= 0
end
function M.bit32.lrotate(x, disp)
return lrotate(x % MOD, disp)
end
function M.bit32.rrotate(x, disp)
return rrotate(x % MOD, disp)
end
function M.bit32.lshift(x,disp)
if disp > 31 or disp < -31 then return 0 end
return lshift(x % MOD, disp)
end
function M.bit32.rshift(x,disp)
if disp > 31 or disp < -31 then return 0 end
return rshift(x % MOD, disp)
end
function M.bit32.arshift(x,disp)
x = x % MOD
if disp >= 0 then
if disp > 31 then
return (x >= 0x80000000) and MODM or 0
else
local z = rshift(x, disp)
if x >= 0x80000000 then z = z + lshift(math.pow(2, disp-1), 32-disp) end
return z
end
else
return lshift(x, -disp)
end
end
function M.bit32.extract(x, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
x = x % MOD
return extract(x, field, ...)
end
function M.bit32.replace(x, v, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
x = x % MOD
v = v % MOD
return replace(x, v, field, ...)
end
--
-- Start LuaBitOp "bit" compat section.
--
M.bit = {} -- LuaBitOp "bit" compatibility
function M.bit.tobit(x)
x = x % MOD
if x >= 0x80000000 then x = x - MOD end
return x
end
local bit_tobit = M.bit.tobit
function M.bit.tohex(x, ...)
return tohex(x % MOD, ...)
end
function M.bit.bnot(x)
return bit_tobit(bnot(x % MOD))
end
local function bit_bor(a, b, c, ...)
if c then
return bit_bor(bit_bor(a, b), c, ...)
elseif b then
return bit_tobit(bor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bor = bit_bor
local function bit_band(a, b, c, ...)
if c then
return bit_band(bit_band(a, b), c, ...)
elseif b then
return bit_tobit(band(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.band = bit_band
local function bit_bxor(a, b, c, ...)
if c then
return bit_bxor(bit_bxor(a, b), c, ...)
elseif b then
return bit_tobit(bxor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bxor = bit_bxor
function M.bit.lshift(x, n)
return bit_tobit(lshift(x % MOD, n % 32))
end
function M.bit.rshift(x, n)
return bit_tobit(rshift(x % MOD, n % 32))
end
function M.bit.arshift(x, n)
return bit_tobit(arshift(x % MOD, n % 32))
end
function M.bit.rol(x, n)
return bit_tobit(lrotate(x % MOD, n % 32))
end
function M.bit.ror(x, n)
return bit_tobit(rrotate(x % MOD, n % 32))
end
function M.bit.bswap(x)
return bit_tobit(bswap(x % MOD))
end
]]
return M

View File

@ -1,300 +0,0 @@
--[[
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 1].
]]
local function hslToRgb(h, s, l)
local r, g, b
if s == 0 then
r, g, b = l, l, l -- achromatic
else
function hue2rgb(p, q, t)
if t < 0 then t = t + 1 end
if t > 1 then t = t - 1 end
if t < 1/6 then return p + (q - p) * 6 * t end
if t < 1/2 then return q end
if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 end
return p
end
local q
if l < 0.5 then q = l * (1 + s) else q = l + s - l * s end
local p = 2 * l - q
r = hue2rgb(p, q, h + 1/3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1/3)
end
return r, g, b
end
--[[
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 1].
]]
local function rgbToHsl(r, g, b)
local max, min = math.max(r, g, b), math.min(r, g, b)
local h, s, l
l = (max + min) / 2
if max == 0 then s = 0 else s = (max - min) / max end
if max == min then
h, s = 0, 0 -- achromatic
else
local d = max - min
local s
if l > 0.5 then s = d / (2 - max - min) else s = d / (max + min) end
if max == r then
h = (g - b) / d
if g < b then h = h + 6 end
elseif max == g then h = (b - r) / d + 2
elseif max == b then h = (r - g) / d + 4
end
h = h / 6
end
return h, s, l
end
--[[
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 1] and
* returns h, s, and v in the set [0, 1].
]]
local function rgbToHsv(r, g, b)
local max, min = math.max(r, g, b), math.min(r, g, b)
local h, s, v
v = max
local d = max - min
if max == 0 then s = 0 else s = d / max end
if max == min then
h = 0 -- achromatic
else
if max == r then
h = (g - b) / d
if g < b then h = h + 6 end
elseif max == g then h = (b - r) / d + 2
elseif max == b then h = (r - g) / d + 4
end
h = h / 6
end
return h, s, v
end
--[[
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 1].
]]
local function hsvToRgb(h, s, v)
local r, g, b
local i = math.floor(h * 6);
local f = h * 6 - i;
local p = v * (1 - s);
local q = v * (1 - f * s);
local t = v * (1 - (1 - f) * s);
i = i % 6
if i == 0 then r, g, b = v, t, p
elseif i == 1 then r, g, b = q, v, p
elseif i == 2 then r, g, b = p, v, t
elseif i == 3 then r, g, b = p, q, v
elseif i == 4 then r, g, b = t, p, v
elseif i == 5 then r, g, b = v, p, q
end
return r, g, b
end
---@class color
---@field r number @red, 0.0 - 1.0
---@field g number @green, 0.0 - 1.0
---@field b number @blue, 0.0 - 1.0
---@field a number @alpha, 0.0 - 1.0
---@operator add(color): color
---@operator add(number): color
---@operator sub(color): color
---@operator sub(number): color
---@operator mul(color): color
---@operator mul(number): color
---@operator div(color): color
---@operator div(number): color
local col = {}
--- for use in actor:diffuse(col:unpack())
---@return number, number, number, number
function col:unpack()
return self.r, self.g, self.b, self.a
end
-- conversions
---@return number, number, number
function col:rgb()
return self.r, self.g, self.b
end
---@return number, number, number
function col:hsl()
return rgbToHsl(self.r, self.g, self.b)
end
---@return number, number, number
function col:hsv()
return rgbToHsv(self.r, self.g, self.b)
end
---@return string
function col:hex()
return string.format('%02x%02x%02x',
math.floor(self.r * 255),
math.floor(self.g * 255),
math.floor(self.b * 255))
end
-- setters
---@return color
function col:hue(h)
local _, s, v = self:hsv()
return hsv(h % 1, s, v, self.a)
end
---@return color
function col:huesmooth(h)
local _, s, v = self:hsv()
return shsv(h % 1, s, v, self.a)
end
---@return color
function col:alpha(a)
return rgb(self.r, self.g, self.b, a)
end
--- multiplies current alpha by provided value
---@return color
function col:malpha(a)
return rgb(self.r, self.g, self.b, self.a * a)
end
-- effects
---@return color
function col:invert()
return rgb(1 - self.r, 1 - self.g, 1 - self.b, self.a)
end
---@return color
function col:grayscale()
return rgb(self.r * 0.299 + self.g * 0.587 + self.b * 0.114, self.a)
end
---@return color
function col:hueshift(a)
local h, s, v = self:hsv()
return hsv((h + a) % 1, s, v, self.a)
end
local colmeta = {}
function colmeta:__index(i)
if i == 1 then return self.r end
if i == 2 then return self.g end
if i == 3 then return self.b end
if i == 4 then return self.a end
return col[i]
end
local function typ(a)
return (type(a) == 'table' and a.r and a.g and a.b and a.a) and 'color' or type(a)
end
local function genericop(a, b, f, name)
local typea = typ(a)
local typeb = typ(b)
if typea == 'number' then
return rgb(f(b.r, a), f(b.g, a), f(b.b, a), b.a)
elseif typeb == 'number' then
return rgb(f(a.r, b), f(a.g, b), f(a.b, b), a.a)
elseif typea == 'color' and typeb == 'color' then
return rgb(f(a.r, b.r), f(a.g, b.g), f(a.b, b.b), f(a.a, b.a))
end
error('cant apply ' .. name .. ' to ' .. typea .. ' and ' .. typeb, 3)
end
function colmeta.__add(a, b)
return genericop(a, b, function(a, b) return a + b end, 'add')
end
function colmeta.__sub(a, b)
return genericop(a, b, function(a, b) return a - b end, 'sub')
end
function colmeta.__mul(a, b)
return genericop(a, b, function(a, b) return a * b end, 'mul')
end
function colmeta.__div(a, b)
return genericop(a, b, function(a, b) return a / b end, 'div')
end
function colmeta.__eq(a, b)
return (typ(a) == 'color' and typ(b) == 'color') and (a.r == b.r and a.g == b.g and a.b == b.b and a.a == b.a)
end
function colmeta:__tostring()
return '#' .. self:hex()
end
colmeta.__name = 'color'
-- constructors
---@return color
function rgb(r, g, b, a)
a = a or 1
return setmetatable({r = r, g = g, b = b, a = a or 1}, colmeta)
end
---@return color
function hsl(h, s, l, a)
a = a or 1
local r, g, b = hslToRgb(h % 1, s, l)
return setmetatable({r = r, g = g, b = b, a = a or 1}, colmeta)
end
---@return color
function hsv(h, s, v, a)
a = a or 1
local r, g, b = hsvToRgb(h % 1, s, v)
return setmetatable({r = r, g = g, b = b, a = a or 1}, colmeta)
end
--- smoother hsv. not correct but looks nicer
---@return color
function shsv(h, s, v, a)
h = h % 1
return hsv(h * h * (3 - 2 * h), s, v, a)
end
---@param hex string
---@return color
function hex(hex)
hex = string.gsub(hex, '#', '')
if string.len(hex) == 3 then
return rgb((tonumber('0x' .. string.sub(hex, 1, 1)) * 17) / 255, (tonumber('0x' .. string.sub(hex, 2, 2)) * 17) / 255, (tonumber('0x' .. string.sub(hex, 3, 3)) * 17) / 255)
else
return rgb(tonumber('0x' .. string.sub(hex, 1, 2)) / 255, tonumber('0x' .. string.sub(hex, 3, 4)) / 255, tonumber('0x' .. string.sub(hex, 5, 6)) / 255)
end
end

View File

@ -1,80 +0,0 @@
---@class easable
---@field public a number @the eased value
---@field public toa number @the target, uneased value
---@field protected onUpdateFuncs fun():nil[]
local eas = {}
---@param new number @New value to ease to
---@return void
function eas:set(new)
self.toa = new
end
---@param new number @New value
---@return void
function eas:reset(new)
self.toa = new
self.a = new
end
---@param new number @How much to add to current value to ease to
---@return void
function eas:add(new)
self.toa = self.toa + new
end
---@param func fun(a: number):void @Adds a callback function that will run each time the eased value changes
---@return void
function eas:onUpdate(func)
table.insert(self.onUpdateFuncs, func)
end
local easmeta = {}
easmeta.__index = eas
easmeta.__name = 'easable'
function easmeta.__add(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) + ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__sub(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) - ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__mul(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) * ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__div(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) / ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__mod(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) % ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__eq(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) == ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__lt(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) < ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__le(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) <= ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta:__call(dt)
self.a = mix(self.a, self.toa, dt)
for _, callback in ipairs(self.onUpdateFuncs) do
callback(self.a)
end
end
function easmeta:__tostring()
return tostring(self.a)
end
function easmeta:__unm(self)
return -self.a
end
---@param default number
---@return easable
function easable(default)
default = default or 0
return setmetatable({a = default, toa = default, onUpdateFuncs = {}}, easmeta)
end

View File

@ -1,90 +0,0 @@
---@class easable2
---@field public a number @the eased value
---@field public toa number @the target, uneased value
---@field public ease fun(a:number):number @the ease to use
---@field protected onUpdateFuncs fun(a:number):nil[]
---@field protected _a number @the internal value, linearly eased
local eas = {}
---@param new number @New value to ease to
---@return void
function eas:set(new)
self.toa = new
end
---@param new number @New value
---@return void
function eas:reset(new)
self.toa = new
self._a = new
end
---@param new number @How much to add to current value to ease to
---@return void
function eas:add(new)
self.toa = self.toa + new
end
---@param func fun(a: number):void @Adds a callback function that will run each time the eased value changes
---@return void
function eas:onUpdate(func)
table.insert(self.onUpdateFuncs, func)
end
local easmeta = {}
easmeta.__index = eas
easmeta.__name = 'easable2'
function easmeta.__add(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) + ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__sub(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) - ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__mul(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) * ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__div(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) / ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__mod(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) % ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__eq(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) == ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__lt(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) < ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta.__le(a, b)
return ((type(a) == 'table' and a.a) and a.a or a) <= ((type(b) == 'table' and b.a) and b.a or b)
end
function easmeta:__call(dt)
if self._a == self.toa then
-- do nothing
elseif self._a < self.toa then
self._a = self._a + math.min(dt, math.abs(self._a - self.toa))
else
self._a = self._a - math.min(dt, math.abs(self._a - self.toa))
end
self.a = self.ease(self._a)
for _, callback in ipairs(self.onUpdateFuncs) do
callback(self.a)
end
end
function easmeta:__tostring()
return tostring(self.a)
end
function easmeta:__unm(self)
return -self.a
end
---@param default number
---@return easable2
function easable2(default, ease)
default = default or 0
return setmetatable({a = default, toa = default, onUpdateFuncs = {}, ease = ease or outSine, _a = default}, easmeta)
end

View File

@ -1,338 +0,0 @@
-- nabbed straight from mirin template:
-- https://github.com/XeroOl/notitg-mirin/blob/d1e9a8e71026aeabe81c682a114ce265cbd6362a/template/ease.lua
local sqrt = math.sqrt
local sin = math.sin
local asin = math.asin
local cos = math.cos
local pow = math.pow
local exp = math.exp
local pi = math.pi
local abs = math.abs
-- ===================================================================== --
-- Utility functions
--- Flip any easing function, making it go from 1 to 0
-- Example use:
-- ```lua
-- ease {0, 20, flip(outQuad), 50, 'modname'}
-- ```
flip = setmetatable({}, {
__call = function(self, fn)
self[fn] = self[fn] or function(x) return 1 - fn(x) end
return self[fn]
end
})
-- Mix two easing functions together into a new ease
-- the new ease starts by acting like the first argument, and then ends like the second argument
-- Example: ease {0, 20, blendease(inQuad, outQuad), 100, 'modname'}
blendease = setmetatable({}, {
__index = function(self, key)
self[key] = {}
return self[key]
end,
__call = function(self, fn1, fn2)
if not self[fn1][fn2] then
local transient1 = fn1(1) <= 0.5
local transient2 = fn2(1) <= 0.5
if transient1 and not transient2 then
error('blendease: the first argument is a transient ease, but the second argument doesn\'t match')
end
if transient2 and not transient1 then
error('blendease: the second argument is a transient ease, but the first argument doesn\'t match')
end
self[fn1][fn2] = function(x)
local mixFactor = 3*x^2-2*x^3
return (1 - mixFactor) * fn1(x) + mixFactor * fn2(x)
end
end
return self[fn1][fn2]
end
})
local function param1cache(self, param1)
self.cache[param1] = self.cache[param1] or function(x)
return self.fn(x, param1)
end
return self.cache[param1]
end
local param1mt = {
__call = function(self, x, param1)
return self.fn(x, param1 or self.dp1)
end,
__index = {
param = param1cache,
params = param1cache,
}
}
-- Declare an easing function taking one custom parameter
function with1param(fn, defaultparam1)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
cache = {},
}, param1mt)
end
local function param2cache(self, param1, param2)
self.cache[param1] = self.cache[param1] or {}
self.cache[param1][param2] = self.cache[param1][param2] or function(x)
return self.fn(x, param1, param2)
end
return self.cache[param1][param2]
end
local param2mt = {
__call = function(self, x, param1, param2)
return self.fn(x, param1 or self.dp1, param2 or self.dp2)
end,
__index = {
param=param2cache,
params=param2cache,
}
}
-- Declare an easing function taking two custom parameters
function with2params(fn, defaultparam1, defaultparam2)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
dp2 = defaultparam2,
cache = {},
}, param2mt)
end
-- ===================================================================== --
-- Easing functions
function bounce(t) return 4 * t * (1 - t) end
function tri(t) return 1 - abs(2 * t - 1) end
function bell(t) return inOutQuint(tri(t)) end
function pop(t) return 3.5 * (1 - t) * (1 - t) * sqrt(t) end
function tap(t) return 3.5 * t * t * sqrt(1 - t) end
function pulse(t) return t < .5 and tap(t * 2) or -pop(t * 2 - 1) end
function spike(t) return exp(-10 * abs(2 * t - 1)) end
function inverse(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end
local function popElasticInternal(t, damp, count)
return (1000 ^ -(t ^ damp) - 0.001) * sin(count * pi * t)
end
local function tapElasticInternal(t, damp, count)
return (1000 ^ -((1 - t) ^ damp) - 0.001) * sin(count * pi * (1 - t))
end
local function pulseElasticInternal(t, damp, count)
if t < .5 then
return tapElasticInternal(t * 2, damp, count)
else
return -popElasticInternal(t * 2 - 1, damp, count)
end
end
popElastic = with2params(popElasticInternal, 1.4, 6)
tapElastic = with2params(tapElasticInternal, 1.4, 6)
pulseElastic = with2params(pulseElasticInternal, 1.4, 6)
impulse = with1param(function(t, damp)
t = t ^ damp
return t * (1000 ^ -t - 0.001) * 18.6
end, 0.9)
function instant() return 1 end
function linear(t) return t end
function inQuad(t) return t * t end
function outQuad(t) return -t * (t - 2) end
function inOutQuad(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 2
else
return 1 - 0.5 * (2 - t) ^ 2
end
end
function outInQuad(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 2
else
return 0.5 + 0.5 * (t - 1) ^ 2
end
end
function inCubic(t) return t * t * t end
function outCubic(t) return 1 - (1 - t) ^ 3 end
function inOutCubic(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 3
else
return 1 - 0.5 * (2 - t) ^ 3
end
end
function outInCubic(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 3
else
return 0.5 + 0.5 * (t - 1) ^ 3
end
end
function inQuart(t) return t * t * t * t end
function outQuart(t) return 1 - (1 - t) ^ 4 end
function inOutQuart(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 4
else
return 1 - 0.5 * (2 - t) ^ 4
end
end
function outInQuart(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 4
else
return 0.5 + 0.5 * (t - 1) ^ 4
end
end
function inQuint(t) return t ^ 5 end
function outQuint(t) return 1 - (1 - t) ^ 5 end
function inOutQuint(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 5
else
return 1 - 0.5 * (2 - t) ^ 5
end
end
function outInQuint(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 5
else
return 0.5 + 0.5 * (t - 1) ^ 5
end
end
function inExpo(t) return 1000 ^ (t - 1) - 0.001 end
function outExpo(t) return 1.001 - 1000 ^ -t end
function inOutExpo(t)
t = t * 2
if t < 1 then
return 0.5 * 1000 ^ (t - 1) - 0.0005
else
return 1.0005 - 0.5 * 1000 ^ (1 - t)
end
end
function outInExpo(t)
if t < 0.5 then
return outExpo(t * 2) * 0.5
else
return inExpo(t * 2 - 1) * 0.5 + 0.5
end
end
function inCirc(t) return 1 - sqrt(1 - t * t) end
function outCirc(t) return sqrt(-t * t + 2 * t) end
function inOutCirc(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * sqrt(1 - t * t)
else
t = t - 2
return 0.5 + 0.5 * sqrt(1 - t * t)
end
end
function outInCirc(t)
if t < 0.5 then
return outCirc(t * 2) * 0.5
else
return inCirc(t * 2 - 1) * 0.5 + 0.5
end
end
function outBounce(t)
if t < 1 / 2.75 then
return 7.5625 * t * t
elseif t < 2 / 2.75 then
t = t - 1.5 / 2.75
return 7.5625 * t * t + 0.75
elseif t < 2.5 / 2.75 then
t = t - 2.25 / 2.75
return 7.5625 * t * t + 0.9375
else
t = t - 2.625 / 2.75
return 7.5625 * t * t + 0.984375
end
end
function inBounce(t) return 1 - outBounce(1 - t) end
function inOutBounce(t)
if t < 0.5 then
return inBounce(t * 2) * 0.5
else
return outBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function outInBounce(t)
if t < 0.5 then
return outBounce(t * 2) * 0.5
else
return inBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function inSine(x) return 1 - cos(x * (pi * 0.5)) end
function outSine(x) return sin(x * (pi * 0.5)) end
function inOutSine(x)
return 0.5 - 0.5 * cos(x * pi)
end
function outInSine(t)
if t < 0.5 then
return outSine(t * 2) * 0.5
else
return inSine(t * 2 - 1) * 0.5 + 0.5
end
end
function outElasticInternal(t, a, p)
return a * pow(2, -10 * t) * sin((t - p / (2 * pi) * asin(1/a)) * 2 * pi / p) + 1
end
local function inElasticInternal(t, a, p)
return 1 - outElasticInternal(1 - t, a, p)
end
function inOutElasticInternal(t, a, p)
return t < 0.5
and 0.5 * inElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * outElasticInternal(t * 2 - 1, a, p)
end
function outInElasticInternal(t, a, p)
return t < 0.5
and 0.5 * outElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * inElasticInternal(t * 2 - 1, a, p)
end
inElastic = with2params(inElasticInternal, 1, 0.3)
outElastic = with2params(outElasticInternal, 1, 0.3)
inOutElastic = with2params(inOutElasticInternal, 1, 0.3)
outInElastic = with2params(outInElasticInternal, 1, 0.3)
function inBackInternal(t, a) return t * t * (a * t + t - a) end
function outBackInternal(t, a) t = t - 1 return t * t * ((a + 1) * t + a) + 1 end
function inOutBackInternal(t, a)
return t < 0.5
and 0.5 * inBackInternal(t * 2, a)
or 0.5 + 0.5 * outBackInternal(t * 2 - 1, a)
end
function outInBackInternal(t, a)
return t < 0.5
and 0.5 * outBackInternal(t * 2, a)
or 0.5 + 0.5 * inBackInternal(t * 2 - 1, a)
end
inBack = with1param(inBackInternal, 1.70158)
outBack = with1param(outBackInternal, 1.70158)
inOutBack = with1param(inOutBackInternal, 1.70158)
outInBack = with1param(outInBackInternal, 1.70158)

View File

@ -1,10 +0,0 @@
require('input')
bitop = require('bitop') -- TODO: tons of this is commented out because of '...'. FIX. IT.
require('rng')
require('easable')
require('easable2')
require('color')
require('vector2D')
require('ease')
uwuify = require('uwuify')
require('util')

View File

@ -1,39 +0,0 @@
inputs = { -- -1 for not pressed, time for time of press
Left = -1,
Down = -1,
Up = -1,
Right = -1
}
rawInputs = {
Left = -1,
Down = -1,
Up = -1,
Right = -1
}
directions = {
Left = {-1, 0},
Down = {0, 1},
Up = {0, -1},
Right = {1, 0}
}
function uranium.init()
for pn = 1, 2 do
for i,j in ipairs({'Left', 'Down', 'Up', 'Right'}) do
local i = i -- lua scope funnies
local j = j
_main:addcommand('StepP' .. pn .. j .. 'PressMessage', function()
rawInputs[j] = t
if uranium:call('press', j) then return end
inputs[j] = t
end)
_main:addcommand('StepP' .. pn .. j .. 'LiftMessage', function()
if uranium:call('release', j) then return end
inputs[j] = -1
rawInputs[j] = -1
end)
end
end
end

View File

@ -1,172 +0,0 @@
-- xoshiro128** 1.1 by David Blackman and Sebastiano Vigna (vigna@acm.org) https://prng.di.unimi.it/xoshiro128starstar.c
-- Lua implementation by Jill "oatmealine" Monoids
-- Licensed under CC-BY-SA
local RAND_MAX = 4294967295
---@param x int
---@param k int
local function rotl(x, k)
return bitop.bor(bitop.lshift(x, k), bitop.rshift(x, (32 - k)))
end
---@param state int[] @array of size 4; will be mutated
local function next(state)
local result = rotl(state[2] * 5, 7) * 9
local t = bitop.lshift(state[2], 9)
state[3] = bitop.bxor(state[3], state[1])
state[4] = bitop.bxor(state[4], state[2])
state[2] = bitop.bxor(state[2], state[3])
state[1] = bitop.bxor(state[1], state[4])
state[2] = bitop.bxor(state[3], t)
state[3] = rotl(state[4], 11)
return result
end
local JUMP = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }
---@param state int[] @array of size 4; will be mutated
local function jump(state)
local s0 = 0
local s1 = 0
local s2 = 0
local s3 = 0
for _, j in ipairs(JUMP) do
for b = 0, 31 do
if bitop.band(j, bitop.lshift(1, b)) ~= 0 then
s0 = bitop.bxor(s0, state[1])
s1 = bitop.bxor(s1, state[2])
s2 = bitop.bxor(s2, state[3])
s3 = bitop.bxor(s3, state[4])
end
next(state)
end
end
state[1] = s0
state[2] = s1
state[3] = s2
state[4] = s3
end
local LONG_JUMP = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 }
---@param state int[] @array of size 4; will be mutated
local function long_jump(state)
local s0 = 0
local s1 = 0
local s2 = 0
local s3 = 0
for _, j in ipairs(LONG_JUMP) do
for b = 0, 31 do
if bitop.band(j, bitop.lshift(1, b)) ~= 0 then
s0 = bitop.bxor(s0, state[1])
s1 = bitop.bxor(s1, state[2])
s2 = bitop.bxor(s2, state[3])
s3 = bitop.bxor(s3, state[4])
end
next(state)
end
end
state[1] = s0
state[2] = s1
state[3] = s2
state[4] = s3
end
---@class rng a xoshiro128** pseudorandom implementation
---@field public state int[] the current state, size of 4
rng = {}
--- gets the next pseudo-random value; recommended to use abstractions (like __call) over this
---@return int
function rng:next()
return next(self.state)
end
--- This is the jump function for the generator. It is equivalent
--- to 2^64 calls to next(); it can be used to generate 2^64
--- non-overlapping subsequences for parallel computations.
---@return void
function rng:jump()
return jump(self.state)
end
--- This is the long-jump function for the generator. It is equivalent to
--- 2^96 calls to next(); it can be used to generate 2^32 starting points,
--- from each of which jump() will generate 2^32 non-overlapping
--- subsequences for parallel distributed computations.
---@return void
function rng:longJump()
return long_jump(self.state)
end
--- if `max` is not given, `min` will be used as the maximum and the minimum will be 1
--- if min is 1 and max is 4, the returned value can be 1, 2, 3 or 4
---@param min int
---@param max int
---@return int
function rng:int(min, max)
if not max then
local m = min
min = 1
max = m
end
local _min = min
local _max = max
min = math.min(_min, _max)
max = math.max(_min, _max)
return min + (self:next() % (max - min + 1))
end
--- if `max` is not given, it will be 1
---@param max float
---@return float
function rng:float(max)
return ((self:next() % RAND_MAX) / RAND_MAX) * (max or 1)
end
---@return boolean
function rng:bool()
return self:next() % 2 == 0
end
function rng:seed(seed)
self.state = {seed, seed, seed, seed}
self:next()
end
local rngmeta = {}
--- acts identical to math.random()
function rngmeta:__call(a, b)
if a then
return self:int(a, b)
end
return self:float()
end
rngmeta.__index = rng
--- creates a new RNG object
---@param seed int[] @array of size 4, will default to os.time() if not given
---@return rng
function rng.init(seed)
seed = seed or os.time()
local state = {seed, seed, seed, seed}
local this = setmetatable({state = state}, rngmeta)
this:next() -- just to prevent the state from being all the same number; i dont know a cleaner way of doing this
return this
end
return rng

View File

@ -1,157 +0,0 @@
---@param a number
---@param b number
---@param x number
---@return number
function mix(a, b, x)
return a * (1 - x) + b * x
end
lerp = mix
---@param x number
---@return number
function sign(x)
if x > 0 then return 1 end
if x < 0 then return -1 end
return 0
end
function deepcopy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[deepcopy(k)] = deepcopy(v) end
return res
end
---@param a number
---@param x number
---@param y number
---@return number
function clamp(a, x, y)
return math.max(math.min(a, math.max(x, y)), math.min(x, y))
end
---@param tab any[]
---@param elem any
function includes(tab, elem)
if not tab then error('bad argument #1 (expected table, got nil)', 2) end
for _, v in pairs(tab) do
if elem == v then return true end
end
return false
end
---@param text string
---@param length int
function truncate(text, length)
local addellipses = false
local firstLine = nil
for line in string.gfind(text, '([^\n]*)\n?') do
if not firstLine then
firstLine = line
elseif line ~= '' then
addellipses = true
break
end
end
text = firstLine
if string.len(text) > length then
text = string.sub(text, 1, length)
addellipses = true
end
if addellipses then text = text .. '...' end
return text
end
function padLeft(str, num, fill)
local s = {}
for i = 1, num - #str do
table.insert(s, fill)
end
table.insert(s, str)
return table.concat(s, '')
end
function padLeft(str, num, fill)
local s = {}
table.insert(s, str)
for i = 1, num - #str do
table.insert(s, fill)
end
return table.concat(s, '')
end
local whitespaces = {' ', '\n', '\r'}
---@param str string
function trimLeft(str)
while includes(whitespaces, string.sub(str, 1, 1)) do
str = string.sub(str, 2)
end
return str
end
---@param str string
function trimRight(str)
while includes(whitespaces, string.sub(str, -1, -1)) do
str = string.sub(str, 1, -2)
end
return str
end
---@param str string
function trim(str)
return trimRight(trimLeft(str))
end
---@param o any
---@return string
--- stringify an object
function fullDump(o, r)
if type(o) == 'table' and (not r or r > 0) then
local s = '{'
local first = true
for k,v in pairs(o) do
if not first then
s = s .. ', '
end
local nr = nil
if r then
nr = r - 1
end
if type(k) ~= 'number' then
s = s .. tostring(k) .. ' = ' .. fullDump(v, nr)
else
s = s .. fullDump(v, nr)
end
first = false
end
return s .. '}'
elseif type(o) == 'string' then
return '"' .. o .. '"'
else
return tostring(o)
end
end
---@param t1 any[]
---@param t2 any[]
---@return any[]
function tableConcat(t1, t2)
for i = 1, #t2 do
t1[#t1 + 1] = t2[i]
end
return t1
end
---@param tab table
function clearMetatables(tab)
setmetatable(tab, nil)
for _, obj in pairs(tab) do
if type(obj) == 'table' then
clearMetatables(obj)
end
end
end

View File

@ -1,23 +0,0 @@
local endings = {'rawr x3', 'OwO', 'UwU', 'o.O', '-.-', '>w<', '(˘ω˘)', 'σωσ', 'ʘwʘ', ':3', 'XD', 'nyaa~~', 'mya', '>_<', 'rawr', '^^', '^^;;', '(^•ω•^)'}
return function(str)
str = string.lower(str)
str = string.gsub(str, 'small', 'smol')
str = string.gsub(str, 'cute', 'kawaii~')
str = string.gsub(str, 'fluff', 'floof')
str = string.gsub(str, 'love', 'luv')
str = string.gsub(str, 'stupid', 'baka')
str = string.gsub(str, 'meow', 'nya~')
str = string.gsub(str, 'l', 'w')
str = string.gsub(str, 'r', 'w')
str = string.gsub(str, 'n([aeiou])', 'ny%1')
str = string.gsub(str, '[.!?]%s', function(e) return e .. endings[math.random(1, #endings)] .. ' ' end)
str = string.gsub(str, '(%s)(%a)(%a)', function(space, rep, other) if math.random() < 0.05 then return space .. rep .. '-' .. rep .. other else return space .. rep .. other end end)
return str
end

View File

@ -1,142 +0,0 @@
---@class vector2D A vector can be defined as a set of 2 coordinates. They can be obtained by doing either vect.x and vect.y or vect[1] and vect[2], for compatibility purposes.
---The reason such a simple class exists is to do simplified math with it - math abstracted as :length(), :angle(), etc is much easier to read.
---@field public x number @x coordinate
---@field public y number @y coordinate
---@operator add(vector2D): vector2D
---@operator add(number): vector2D
---@operator sub(vector2D): vector2D
---@operator sub(number): vector2D
---@operator mul(vector2D): vector2D
---@operator mul(number): vector2D
---@operator div(vector2D): vector2D
---@operator div(number): vector2D
---@operator unm: vector2D
local vect = {}
---@return number
function vect:length()
return math.sqrt(self.x * self.x + self.y * self.y)
end
---@return number
function vect:lengthSquared()
return self.x * self.x + self.y * self.y
end
---@return number
function vect:angle()
return math.atan2(self.y, self.x)
end
---@return vector2D
function vect:normalize()
local len = self:length()
if len ~= 0 and len ~= 1 then
return vector2D(self.x / len, self.y / len)
else
return self
end
end
---@return vector2D
function vect:resize(x)
local n = self:normalize()
return vector2D(n.x * x, n.y * x)
end
---@return vector2D
function vect:rotate(ang)
local a = self:angle()
local len = self:length()
return vectorFromAngle(a + ang, len)
end
---@return number, number
function vect:unpack()
return self.x, self.y
end
---@param v2 vector2D
---@return number
function vect:distance(v2)
return (self - v2):length()
end
---@param v2 vector2D
---@return number
function vect:distanceSquared(v2)
return (self - v2):lengthSquared()
end
local vectmeta = {}
local function typ(a)
return (type(a) == 'table' and a.x and a.y) and 'vector' or type(a)
end
local function genericop(a, b, f, name)
local typea = typ(a)
local typeb = typ(b)
if typea == 'number' then
return vector2D(f(b.x, a), f(b.y, a))
elseif typeb == 'number' then
return vector2D(f(a.x, b), f(a.y, b))
elseif typea == 'vector' and typeb == 'vector' then
return vector2D(f(a.x, b.x), f(a.y, b.y))
end
error('cant apply ' .. name .. ' to ' .. typea .. ' and ' .. typeb, 3)
end
function vectmeta.__add(a, b)
return genericop(a, b, function(a, b) return a + b end, 'add')
end
function vectmeta.__sub(a, b)
return genericop(a, b, function(a, b) return a - b end, 'sub')
end
function vectmeta.__mul(a, b)
return genericop(a, b, function(a, b) return a * b end, 'mul')
end
function vectmeta.__div(a, b)
return genericop(a, b, function(a, b) return a / b end, 'div')
end
function vectmeta.__eq(a, b)
return (typ(a) == 'vector' and typ(b) == 'vector') and (a.x == b.x and a.y == b.y)
end
function vectmeta:__unm()
return vector2D(-self.x, -self.y)
end
function vectmeta:__tostring()
return '(' .. self.x .. ', ' .. self.y .. ')'
end
vectmeta.__name = 'vector'
function vectmeta:__index(i)
if i == 1 then return self.x end
if i == 2 then return self.y end
return vect[i]
end
--- create a new vector
---@param x number | nil
---@param y number | nil
---@return vector2D
function vector2D(x, y)
x = x or 0
y = y or x
return setmetatable({x = x, y = y}, vectmeta)
end
--- create a new vector from an angle
---@param ang number | nil @angle in degrees
---@param amp number | nil
---@return vector2D
function vectorFromAngle(ang, amp)
ang = math.rad(ang or 0)
amp = amp or 1
return vector2D(math.cos(ang) * amp, math.sin(ang) * amp)
end
vector = vector2D

View File

@ -1,85 +0,0 @@
---@meta
-- cleaning up some notitg typing jank... ehe
---@alias int number
---@alias float number
---@alias Quad Actor
---@alias void nil
---@return Quad
--- Defines a Quad actor.
function Quad() end
---@return ActorProxy
--- Defines an ActorProxy actor.
function ActorProxy() end
---@return Polygon
--- Defines a Polygon actor.
function Polygon() end
---@param file string
---@return Sprite
--- Defines a Sprite actor.
function Sprite(file) end
---@param file string
---@return Model
--- Defines a Model actor.
function Model(file) end
---@param font string?
---@param text string?
---@return BitmapText
--- Defines a BitmapText actor.
function BitmapText(font, text) end
---@param file string
---@return ActorSound
--- Defines an ActorSound actor.
function ActorSound(file) end
---@param actor Actor
--- Resets an actor to its initial state
function reset(actor) end
---@type number
--- A simple timer. Ticks upwards at a rate of 1/sec.
---
--- **The start time is undefined!** This uses `os.clock()`, meaning this will be inconsistent between modfile starts.
---
--- It's recommended to only use this for eg. `math.sin`, rotations, and other similar visual effects. If you want a proper timer, see `b`.
t = 0
---@type number
--- The amount of beats that have passed since the start of the file.
b = 0
---@type ActorFrame
--- The root ActorFrame. Use this for `addcommand` and similar!
_main = {}
---@type number
--- The center of the screen on the X axis. Equal to `SCREEN_CENTER_X`.
scx = 0
---@type number
--- The center of the screen on the Y axis. Equal to `SCREEN_CENTER_Y`.
scy = 0
---@type number
--- The screen width. Equal to `SCREEN_WIDTH`.
sw = 0
---@type number
--- The screen height. Equal to `SCREEN_HEIGHT`.
sh = 0
--- The Uranium Template table! Mostly callback-related stuff goes here.
uranium = {}
--- A callback for initialization. Called on `OnCommand`.
uranium.init = function() end
--- A callback for updates. Called every frame. Draw stuff here!
uranium.update = function() end
---@param event string
---@param ... any
---@return any
--- Call a defined callback.
function uranium:call(event, ...) end
--- Equivalent to a modfile-sandboxed `_G`, similar to Mirin's `xero`. You shouldn't need this; and if you do, *what are you doing?*
---@type table<any, any>
oat = {}