mirin. module. mirin. module
This commit is contained in:
parent
4f473a744a
commit
3d55d3daeb
2
main.xml
2
main.xml
|
@ -19,7 +19,7 @@
|
|||
-- https://github.com/XeroOl/notitg-mirin/blob/0fbff2ee93d905feeb58c4aac4fe7f5f9ebc9647/template/std.lua#L17
|
||||
oat.package = {
|
||||
-- uranium template loader path
|
||||
path = 'src/?.lua;src/?/init.lua;template/?.lua',
|
||||
path = 'src/?.lua;src/?/init.lua;template/?.lua;template/?/init.lua',
|
||||
preload = {},
|
||||
loaded = {},
|
||||
loaders = {
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
---@diagnostic disable: lowercase-global
|
||||
-- Convenience shortcuts
|
||||
|
||||
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
|
||||
|
||||
xero()
|
||||
-- ===================================================================== --
|
||||
|
||||
-- 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)
|
|
@ -0,0 +1,19 @@
|
|||
xero = oat
|
||||
xero.foreground = oat._main
|
||||
xero.MIRIN_VERSION = 'URANIUM-5.0.1'
|
||||
|
||||
|
||||
-- Load all of the core .lua files
|
||||
-- The order DOES matter here:
|
||||
-- std.lua needs to be loaded first
|
||||
-- template.lua needs to be last
|
||||
require('stdlib.mirin.std')
|
||||
require('stdlib.mirin.sort')
|
||||
require('stdlib.mirin.ease')
|
||||
require('stdlib.mirin.template')
|
||||
|
||||
local xeroActorsAF = Quad()
|
||||
|
||||
function uranium.init()
|
||||
xero.init_command(xeroActorsAF)
|
||||
end
|
|
@ -0,0 +1,152 @@
|
|||
--[[
|
||||
this is based on code from Dirk Laurie and Steve Fisher,
|
||||
used under license as follows:
|
||||
|
||||
|
||||
Copyright © 2013 Dirk Laurie and Steve Fisher.
|
||||
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.
|
||||
|
||||
(modifications by Max Cahill 2018, 2020)
|
||||
(modifications by XeroOl 2021)
|
||||
|
||||
Found at: https://github.com/1bardesign/batteries/blob/master/sort.lua
|
||||
]]
|
||||
|
||||
-- `sort` object, container of the following methods
|
||||
local sort = {}
|
||||
-- Tunable threshold, deciding between the insertion sort and merge sort
|
||||
sort.max_chunk_size = 32
|
||||
|
||||
|
||||
-- ===================================================================== --
|
||||
|
||||
-- Internal implementations
|
||||
|
||||
|
||||
-- Insertion sort on a section of an array
|
||||
function sort._insertion_sort_impl(array, first, last, less)
|
||||
for i = first + 1, last do
|
||||
local k = first
|
||||
local v = array[i]
|
||||
for j = i, first + 1, -1 do
|
||||
if less(v, array[j - 1]) then
|
||||
array[j] = array[j - 1]
|
||||
else
|
||||
k = j
|
||||
break
|
||||
end
|
||||
end
|
||||
array[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Merge sort on two sorted portions of an array
|
||||
function sort._merge(array, workspace, low, middle, high, less)
|
||||
local i, j, k
|
||||
i = 1
|
||||
-- copy first half of array to auxiliary array
|
||||
for j = low, middle do
|
||||
workspace[i] = array[j]
|
||||
i = i + 1
|
||||
end
|
||||
-- sieve through
|
||||
i = 1
|
||||
j = middle + 1
|
||||
k = low
|
||||
while true do
|
||||
if (k >= j) or (j > high) then
|
||||
break
|
||||
end
|
||||
if less(array[j], workspace[i]) then
|
||||
array[k] = array[j]
|
||||
j = j + 1
|
||||
else
|
||||
array[k] = workspace[i]
|
||||
i = i + 1
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
-- copy back any remaining elements of first half
|
||||
for k = k, j - 1 do
|
||||
array[k] = workspace[i]
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Recursive merge sort implementation
|
||||
function sort._merge_sort_impl(array, workspace, low, high, less)
|
||||
if high - low <= sort.max_chunk_size then
|
||||
sort._insertion_sort_impl(array, low, high, less)
|
||||
else
|
||||
local middle = math.floor((low + high) / 2)
|
||||
sort._merge_sort_impl(array, workspace, low, middle, less)
|
||||
sort._merge_sort_impl(array, workspace, middle + 1, high, less)
|
||||
sort._merge(array, workspace, low, middle, high, less)
|
||||
end
|
||||
end
|
||||
|
||||
-- Default comparison function: sort from smallest to biggest
|
||||
local function default_less(a, b)
|
||||
return a < b
|
||||
end
|
||||
|
||||
-- Setup a sorting algorithm, check the validity of the comparator,
|
||||
-- and determine if a case is trivial (no sorting needed)
|
||||
function sort._sort_setup(array, less)
|
||||
less = less or default_less
|
||||
local n = #array
|
||||
--trivial cases; empty or 1 element
|
||||
local trivial = (n <= 1)
|
||||
if not trivial then
|
||||
--check less
|
||||
if less(array[1], array[1]) then
|
||||
error('invalid order function for sorting; less(v, v) should not be true for any v.')
|
||||
end
|
||||
end
|
||||
--setup complete
|
||||
return trivial, n, less
|
||||
end
|
||||
|
||||
-- Public method: merge sort on an array. If the array length is
|
||||
-- less than `max_chunk_size`, an insertion sort will be done instead.
|
||||
function sort.stable_sort(array, less)
|
||||
--setup
|
||||
local trivial, n, less = sort._sort_setup(array, less)
|
||||
if not trivial then
|
||||
--temp storage; allocate ahead of time
|
||||
local workspace = {}
|
||||
local middle = math.ceil(n / 2)
|
||||
workspace[middle] = array[1]
|
||||
--dive in
|
||||
sort._merge_sort_impl( array, workspace, 1, n, less )
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
-- Public method (currently not exposed): insertion sort
|
||||
function sort.insertion_sort(array, less)
|
||||
--setup
|
||||
local trivial, n, less = sort._sort_setup(array, less)
|
||||
if not trivial then
|
||||
sort._insertion_sort_impl(array, 1, n, less)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
-- Exports
|
||||
xero.unstable_sort = table.sort
|
||||
xero.stable_sort = sort.stable_sort
|
|
@ -0,0 +1,249 @@
|
|||
-- Environments
|
||||
--[[
|
||||
-- Create the xero environment, in which everything is
|
||||
setmetatable(xero, {
|
||||
-- if something isn't found in the xero table, fall back to a _G lookup
|
||||
__index = _G,
|
||||
|
||||
-- Calling xero() sets the environment of the current function
|
||||
-- Calling xero(func) returns `func` with the xero environment applied
|
||||
__call = function(self, f)
|
||||
setfenv(f or 2, self)
|
||||
return f
|
||||
end
|
||||
})
|
||||
|
||||
-- make require work
|
||||
xero.package = {
|
||||
-- mirin template loader path
|
||||
path = 'lua/?.lua;lua/?/init.lua;plugins/?.lua;plugins/?/init.lua',
|
||||
preload = {},
|
||||
loaded = {},
|
||||
loaders = {
|
||||
function(modname)
|
||||
local preload = xero.package.preload[modname]
|
||||
return preload or 'no field xero.package.preload[\''..modname..'\']'
|
||||
end,
|
||||
function(modname)
|
||||
local errors = {}
|
||||
-- get the filename
|
||||
local filename = string.gsub(modname, '%.', '/')
|
||||
for path in (string.gfind or string.gmatch)(xero.package.path, '[^;]+') do
|
||||
-- get the file path
|
||||
local filepath = xero.dir .. string.gsub(path, '%?', filename)
|
||||
-- check if file exists
|
||||
if not GAMESTATE:GetFileStructure(filepath) then
|
||||
table.insert(errors, 'no file \''..filepath..'\'')
|
||||
else
|
||||
local loader, err = loadfile(filepath)
|
||||
-- check if file loads properly
|
||||
if err then
|
||||
error(err, 3)
|
||||
elseif loader then
|
||||
return xero(loader)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(errors, '\n')
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
function xero.require(modname)
|
||||
local loaded = xero.package.loaded
|
||||
if not loaded[modname] then
|
||||
local errors = {'module \''..modname..'\' not found:'}
|
||||
local chunk
|
||||
for _, loader in ipairs(xero.package.loaders) do
|
||||
local result = loader(modname)
|
||||
if type(result) == 'string' then
|
||||
table.insert(errors, result)
|
||||
elseif type(result) == 'function' then
|
||||
chunk = result
|
||||
break
|
||||
end
|
||||
end
|
||||
if not chunk then
|
||||
error(table.concat(errors, '\n'), 2)
|
||||
end
|
||||
loaded[modname] = chunk()
|
||||
if loaded[modname] == nil then
|
||||
loaded[modname] = true
|
||||
end
|
||||
end
|
||||
return loaded[modname]
|
||||
end
|
||||
|
||||
-- Apply the environment. :)
|
||||
xero()
|
||||
|
||||
--- Create the strict environment, forbidding creating any variable
|
||||
--- This environment is not related to the xero table, and won't fetch
|
||||
--- values from it, unless explicitely prefixed with `xero.`
|
||||
xero.strict = setmetatable({}, {
|
||||
-- Allow access to _G elements, containing all game methods
|
||||
__index = _G,
|
||||
-- Prevent creating any variable
|
||||
__newindex = function(s, t)
|
||||
error(t)
|
||||
end
|
||||
})
|
||||
|
||||
]]
|
||||
-- ===================================================================== --
|
||||
|
||||
-- Utility functions
|
||||
|
||||
|
||||
--- Returns a shallow copy of the table `src`
|
||||
function copy(src)
|
||||
local dest = {}
|
||||
for k, v in pairs(src) do
|
||||
dest[k] = v
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
-- Clear a table's contents, leaving it empty.
|
||||
-- Useful for resetting a table containing metatables.
|
||||
function clear(t)
|
||||
for k, v in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Clear a table's contents, when the table only contains 'logical' indexes
|
||||
-- (as in: contiguous numerical indexes from 1 to #table)
|
||||
function iclear(t)
|
||||
for i = 1, #t do
|
||||
table.remove(t)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Move global functions to the xero table, allowing for slightly faster
|
||||
-- performance due to not having to go back and forth between xero and _G.
|
||||
--[[
|
||||
xero.xero = _G.xero
|
||||
xero.type = _G.type
|
||||
xero.print = _G.print
|
||||
xero.pairs = _G.pairs
|
||||
xero.ipairs = _G.ipairs
|
||||
xero.unpack = _G.unpack
|
||||
xero.tonumber = _G.tonumber
|
||||
xero.tostring = _G.tostring
|
||||
xero.math = copy(_G.math)
|
||||
xero.table = copy(_G.table)
|
||||
xero.string = copy(_G.string)
|
||||
]]
|
||||
|
||||
-- ===================================================================== --
|
||||
|
||||
-- Data structure for all the `func` declarations.
|
||||
-- This custom data structure smartly handles func priorities, so the order
|
||||
-- they're declared in mods.xml is respected no matter what.
|
||||
-- This data structure is generic enough to be used for any context, but
|
||||
-- that is not the case for now.
|
||||
|
||||
|
||||
local methods = {}
|
||||
|
||||
function methods:add(obj)
|
||||
local stage = self.stage
|
||||
self.n = self.n + 1
|
||||
stage.n = stage.n + 1
|
||||
stage[stage.n] = obj
|
||||
end
|
||||
|
||||
function methods:remove()
|
||||
local swap = self.swap
|
||||
swap[swap.n] = nil
|
||||
swap.n = swap.n - 1
|
||||
self.n = self.n - 1
|
||||
end
|
||||
|
||||
function methods:next()
|
||||
if self.n == 0 then return end
|
||||
|
||||
local swap = self.swap
|
||||
local stage = self.stage
|
||||
local list = self.list
|
||||
|
||||
if swap.n == 0 then
|
||||
stable_sort(stage, self.reverse_comparator)
|
||||
end
|
||||
if stage.n == 0 then
|
||||
if list.n == 0 then
|
||||
while swap.n ~= 0 do
|
||||
list.n = list.n + 1
|
||||
list[list.n] = swap[swap.n]
|
||||
swap[swap.n] = nil
|
||||
swap.n = swap.n - 1
|
||||
end
|
||||
else
|
||||
swap.n = swap.n + 1
|
||||
swap[swap.n] = list[list.n]
|
||||
list[list.n] = nil
|
||||
list.n = list.n - 1
|
||||
end
|
||||
else
|
||||
if list.n == 0 then
|
||||
swap.n = swap.n + 1
|
||||
swap[swap.n] = stage[stage.n]
|
||||
stage[stage.n] = nil
|
||||
stage.n = stage.n - 1
|
||||
else
|
||||
if self.comparator(list[list.n], stage[stage.n]) then
|
||||
swap.n = swap.n + 1
|
||||
swap[swap.n] = list[list.n]
|
||||
list[list.n] = nil
|
||||
list.n = list.n - 1
|
||||
else
|
||||
swap.n = swap.n + 1
|
||||
swap[swap.n] = stage[stage.n]
|
||||
stage[stage.n] = nil
|
||||
stage.n = stage.n - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return swap[swap.n]
|
||||
end
|
||||
|
||||
local mt = {__index = methods}
|
||||
|
||||
function perframe_data_structure(comparator)
|
||||
return setmetatable({
|
||||
comparator = comparator,
|
||||
reverse_comparator = function(a, b) return comparator(b, a) end,
|
||||
stage = {n = 0},
|
||||
list = {n = 0},
|
||||
swap = {n = 0},
|
||||
n = 0,
|
||||
}, mt)
|
||||
end
|
||||
|
||||
-- the behavior of a stringbuilder
|
||||
local stringbuilder_mt = {
|
||||
__index = {
|
||||
-- :build() method converts a stringbuilder into a string, with optional delimiter
|
||||
build = table.concat,
|
||||
-- :clear() method empties the stringbuilder
|
||||
clear = iclear,
|
||||
},
|
||||
|
||||
-- calling a stringbuilder appends to it
|
||||
__call = function(self, a)
|
||||
table.insert(self, tostring(a))
|
||||
return self
|
||||
end,
|
||||
|
||||
-- stringbuilder can convert to a string
|
||||
__tostring = table.concat,
|
||||
}
|
||||
|
||||
-- stringbuilder constructor
|
||||
function stringbuilder()
|
||||
return setmetatable({}, stringbuilder_mt)
|
||||
end
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue