2022-09-19 15:42:33 +02:00
|
|
|
<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
|
|
|
|
|
2022-09-19 17:55:07 +02:00
|
|
|
local actorsInitialized = false -- if true, no new actors can be created
|
|
|
|
|
2022-09-19 15:42:33 +02:00
|
|
|
local luaobj
|
|
|
|
|
|
|
|
local globalQueue = {} -- for resetting
|
2022-09-19 17:55:07 +02:00
|
|
|
|
|
|
|
local patchedFunctions = {}
|
2022-09-19 15:42:33 +02:00
|
|
|
local function patchFunction(f, obj)
|
2022-09-19 17:55:07 +02:00
|
|
|
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
|
2022-09-19 15:42:33 +02:00
|
|
|
end
|
2022-09-19 17:55:07 +02:00
|
|
|
return patchedFunctions[f][obj]
|
2022-09-19 15:42:33 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
local function onCommand(self)
|
2022-09-19 17:55:07 +02:00
|
|
|
actorsInitialized = true
|
2022-09-19 15:42:33 +02:00
|
|
|
uranium:call('init')
|
|
|
|
end
|
|
|
|
|
2022-09-19 17:55:07 +02:00
|
|
|
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
|
|
|
|
|
2022-09-19 15:42:33 +02:00
|
|
|
-- 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)
|
2022-09-19 17:55:07 +02:00
|
|
|
local nextChild = self(2)
|
|
|
|
self:SetDrawFunction(function()
|
|
|
|
if nextChild then
|
|
|
|
nextChild:Draw()
|
|
|
|
end
|
|
|
|
end)
|
2022-09-19 15:42:33 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
local function createProxyActor(name)
|
|
|
|
local queue = {}
|
|
|
|
local initCommands = {}
|
|
|
|
local lockedActor
|
|
|
|
|
|
|
|
return setmetatable({}, {
|
|
|
|
__index = function(self, key)
|
2022-09-19 17:55:07 +02:00
|
|
|
if key == '__raw' then
|
|
|
|
return lockedActor
|
|
|
|
end
|
2022-09-19 15:42:33 +02:00
|
|
|
if lockedActor then
|
|
|
|
local val = lockedActor[key]
|
|
|
|
if type(val) == 'function' then
|
2022-09-19 17:55:07 +02:00
|
|
|
return patchFunction(val, lockedActor)
|
2022-09-19 15:42:33 +02:00
|
|
|
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()
|
2022-09-19 17:55:07 +02:00
|
|
|
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
|
2022-09-19 15:42:33 +02:00
|
|
|
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)
|
2022-09-19 17:55:07 +02:00
|
|
|
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
|
2022-09-19 15:42:33 +02:00
|
|
|
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)
|
2022-09-19 17:55:07 +02:00
|
|
|
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
|
2022-09-19 15:42:33 +02:00
|
|
|
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)
|
2022-09-19 17:55:07 +02:00
|
|
|
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
|
2022-09-19 15:42:33 +02:00
|
|
|
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)
|
2022-09-19 17:55:07 +02:00
|
|
|
if actorsInitialized then error('uranium: cannot create an actor during runtime!!', 2) end
|
2022-09-19 15:42:33 +02:00
|
|
|
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>
|