直接放入Zframework文件

This commit is contained in:
MrZ_26
2023-12-04 20:02:12 +08:00
parent 8ee7a90eca
commit f38746ff96
36 changed files with 13120 additions and 0 deletions

57
Zframework/background.lua Normal file
View File

@@ -0,0 +1,57 @@
local gc_clear=love.graphics.clear
local BGs={
none={draw=function() gc_clear(.08,.08,.084) end}
}
local BGlist={'none'}
local BG={
default='none',
locked=false,
cur='none',
init=false,
resize=false,
update=NULL,
draw=BGs.none.draw,
event=false,
discard=NULL,
}
function BG.lock() BG.locked=true end
function BG.unlock() BG.locked=false end
function BG.add(name,bg)
BGs[name]=bg
BGlist[#BGlist+1]=name
end
function BG.getList()
return BGlist
end
function BG.remList(name)
table.remove(BGlist,TABLE.find(BGlist,name))
end
function BG.send(...)
if BG.event then
BG.event(...)
end
end
function BG.setDefault(bg)
BG.default=bg
end
function BG.set(name,...)
name=name or BG.default
if not BGs[name] or BG.locked then return end
if name~=BG.cur then
BG.discard()
BG.cur=name
local bg=BGs[name]
BG.init= bg.init or NULL
BG.resize= bg.resize or NULL
BG.update= bg.update or NULL
BG.draw= bg.draw or NULL
BG.event= bg.event or NULL
BG.discard=bg.discard or NULL
BG.init()
if ... then BG.send(...) end
end
return true
end
return BG

350
Zframework/bgm.lua Normal file
View File

@@ -0,0 +1,350 @@
local audio=love.audio
local effectsSupported=audio.isEffectsSupported()
local nameList={}
local srcLib={}-- Stored bgm objects: {name='foo', source=bar, ...}, more info at function _addFile()
local lastLoadNames={}
local nowPlay={}
local lastPlay=NONE-- Directly stored last played bgm name(s)
local defaultBGM=false
local maxLoadedCount=3
local volume=1
local function task_setVolume(obj,ve,time,stop)
local vs=obj.vol
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local v=MATH.mix(vs,ve,t)
obj.vol=v
obj.source:setVolume(v*volume)
if t==1 then
obj.volChanging=false
break
end
end
if stop then
obj.source:stop()
end
obj.volChanging=false
return true
end
local function task_setPitch(obj,pe,time)
local ps=obj.pitch
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.pitch=p
obj.source:setPitch(p)
if t==1 then
obj.pitchChanging=false
return true
end
end
end
local function task_setLowgain(obj,pe,time)
local ps=obj.lowgain
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.lowgain=p
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
if t==1 then
obj.lowgainChanging=false
return true
end
end
end
local function task_setHighgain(obj,pe,time)
local ps=obj.highgain
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.highgain=p
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
if t==1 then
obj.highgainChanging=false
return true
end
end
end
local function _clearTask(obj,mode)
local taskFunc=
mode=='volume' and task_setVolume or
mode=='pitch' and task_setPitch or
mode=='lowgain' and task_setLowgain or
mode=='highgain' and task_setHighgain or
'any'
TASK.removeTask_iterate(function(task)
return task.args[1]==obj and (taskFunc=='any' or task.code==taskFunc)
end,obj)
end
local function _updateSources()
local n=#lastLoadNames
while #lastLoadNames>maxLoadedCount and n>0 do
local name=lastLoadNames[n]
if srcLib[name].source and not srcLib[name].source:isPlaying() then
srcLib[name].source=srcLib[name].source:release() and nil
_clearTask(srcLib[name],'any')
end
n=n-1
end
end
local function _addFile(name,path)
if not srcLib[name] then
table.insert(nameList,name)
srcLib[name]={
name=name,path=path,source=false,
vol=0,volChanging=false,
pitch=1,pitchChanging=false,
lowgain=1,lowgainChanging=false,
highgain=1,highgainChanging=false,
}
end
end
local function _tryLoad(name)
if srcLib[name] then
local obj=srcLib[name]
if obj.source then
return true
elseif love.filesystem.getInfo(obj.path) then
obj.source=audio.newSource(obj.path,'stream')
obj.source:setLooping(true)
table.insert(lastLoadNames,1,name)
return true
else
LOG(STRING.repD("Wrong path for BGM '$1': $2",obj.name,obj.path),5)
end
elseif name then
LOG("No BGM: "..name,5)
end
end
local BGM={}
function BGM.getList() return nameList end
function BGM.getCount() return #nameList end
function BGM.setDefault(bgms)
if type(bgms)=='string' then
bgms={bgms}
elseif type(bgms)=='table' then
for i=1,#bgms do assert(type(bgms[i])=='string',"BGM list must be list of strings") end
else
error("BGM.setDefault(bgms): bgms must be string or table")
end
defaultBGM=bgms
end
function BGM.setMaxSources(count)
assert(type(count)=='number' and count>0 and count%1==0,"BGM.setMaxSources(count): count must be positive integer")
maxLoadedCount=count
_updateSources()
end
function BGM.setVol(vol)
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.setVol(vol): count must be in range 0~1")
volume=vol
for i=1,#nowPlay do
local bgm=nowPlay[i]
if not bgm.volChanging then
bgm.source:setVolume(bgm.vol*vol)
end
end
end
function BGM.init(name,path)
if type(name)=='table' then
for k,v in next,name do
_addFile(k,v)
end
else
_addFile(name,path)
end
table.sort(nameList)
LOG(BGM.getCount().." BGM files added")
end
function BGM.play(bgms,args)
if not args then args='' end
if not bgms then bgms=defaultBGM end
if not bgms then return end
if type(bgms)=='string' then bgms={bgms} end
assert(type(bgms)=='table',"BGM.play(name,args): name must be string or table")
if
TABLE.compare(lastPlay,bgms) and
srcLib[lastPlay[1]] and srcLib[lastPlay[1]].source and
srcLib[lastPlay[1]].source:isPlaying()
then
return
end
BGM.stop()
if not STRING.sArg(args,'-preLoad') then
lastPlay=bgms
end
for i=1,#bgms do
local bgm=bgms[i]
assert(type(bgm)=='string',"BGM list can only be list of string")
if _tryLoad(bgm) and not STRING.sArg(args,'-preLoad') then
local obj=srcLib[bgms[i]]
obj.vol=0
obj.pitch=1
obj.lowgain=1
obj.highgain=1
obj.volChanging=false
obj.pitchChanging=false
obj.lowgainChanging=false
obj.highgainChanging=false
_clearTask(obj)
local source=obj.source
source:setLooping(not STRING.sArg(args,'-noloop'))
source:setPitch(1)
source:seek(0)
source:setFilter()
if STRING.sArg(args,'-sdin') then
obj.vol=1
source:setVolume(volume)
BGM.set(bgm,'volume',1,0)
else
source:setVolume(0)
BGM.set(bgm,'volume',1,.626)
end
source:play()
table.insert(nowPlay,obj)
return true
end
end
_updateSources()
end
function BGM.stop(time)
if #nowPlay>0 then
for i=1,#nowPlay do
local obj=nowPlay[i]
_clearTask(obj,'volume')
if time==0 then
obj.source:stop()
obj.volChanging=false
else
TASK.new(task_setVolume,obj,0,time or .626,true)
obj.volChanging=true
end
end
TABLE.cut(nowPlay)
lastPlay=NONE
end
end
---@param mode
---| 'volume'
---| 'lowgain'
---| 'highgain'
---| 'volume'
---| 'pitch'
---| 'seek'
function BGM.set(bgms,mode,...)
if type(bgms)=='string' then
if bgms=='all' then
bgms=nowPlay
else
bgms={srcLib[bgms]}
end
elseif type(bgms)=='table' then
bgms=TABLE.shift(bgms)
for i=1,#bgms do
assert(type(bgms[i])=='string',"BGM list must be list of strings")
bgms[i]=srcLib[bgms[i]]
end
else
error("BGM.play(name,args): name must be string or table")
end
for i=1,#bgms do
local obj=bgms[i]
if obj.source then
if mode=='volume' then
_clearTask(obj,'volume')
local vol,time=...
if not time then time=1 end
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.set(...,volume): volume must be in range 0~1")
assert(type(time)=='number' and time>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setVolume,obj,vol,time)
elseif mode=='pitch' then
_clearTask(obj,'pitch')
local pitch,changeTime=...
if not pitch then pitch=1 end
if not changeTime then changeTime=1 end
assert(type(pitch)=='number' and pitch>0 and pitch<=32,"BGM.set(...,pitch): pitch must be in range 0~32")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setPitch,obj,pitch,changeTime)
elseif mode=='seek' then
local time=...
assert(type(time)=='number',"BGM.set(...,time): time must be number")
obj.source:seek(MATH.clamp(time,0,obj.source:getDuration()))
elseif mode=='lowgain' then
if effectsSupported then
_clearTask(obj,'lowgain')
local lowgain,changeTime=...
if not lowgain then lowgain=1 end
if not changeTime then changeTime=1 end
assert(type(lowgain)=='number' and lowgain>=0 and lowgain<=1,"BGM.set(...,lowgain,highgain): lowgain must be in range 0~1")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setLowgain,obj,lowgain,changeTime)
obj.lowgain=lowgain
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
end
elseif mode=='highgain' then
if effectsSupported then
_clearTask(obj,'highgain')
local highgain,changeTime=...
if not highgain then highgain=1 end
if not changeTime then changeTime=1 end
assert(type(highgain)=='number' and highgain>=0 and highgain<=1,"BGM.set(...,lowgain,highgain): highgain must be in range 0~1")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setHighgain,obj,highgain,changeTime)
obj.highgain=highgain
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
end
else
error("BGM.set(...,mode): mode must be 'volume', 'pitch', or 'seek'")
end
end
end
end
function BGM.getPlaying()
return TABLE.shift(lastPlay)
end
function BGM.isPlaying()
return #nowPlay>0 and nowPlay[1].source:isPlaying()
end
function BGM.tell()
if nowPlay[1] then
return nowPlay[1].source:tell()
end
end
function BGM.getDuration()
if nowPlay[1] then
return nowPlay[1].source:getDuration()
end
end
return BGM

366
Zframework/bitop.lua Normal file
View File

@@ -0,0 +1,366 @@
local M = {_TYPE='module', _NAME='bitop.funcs', _VERSION='1.0-0'}
local floor = math.floor
local MOD = 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, 2 ^ 1)
local op2 = memoize(function(a)
return memoize(function(b)
return op1(a, b)
end)
end)
return make_bitop_uncached(op2, 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 % 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 % 2 ^ 32 / 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 * 2 ^ disp) % 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, 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), 2 ^ width - 1)
end
local extract = M.extract
function M.replace(n, v, field, width) -- Lua5.2 inspired
width = width or 1
local mask1 = 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, 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(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
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(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

235
Zframework/color.lua Normal file
View File

@@ -0,0 +1,235 @@
local abs=math.abs
-- Converts from HSV to RGB color. All arguments should be between 0 and 1, inclusively.
local function HSVToRGB(h,s,v,a)
if s<=0 then return v,v,v,a end
h=h%1*6
local c=v*s
local x=abs((h-1)%2-1)*c
if h<1 then return v,x+v-c,v-c,a
elseif h<2 then return x+v-c,v,v-c,a
elseif h<3 then return v-c,v,x+v-c,a
elseif h<4 then return v-c,x+v-c,v,a
elseif h<5 then return x+v-c,v-c,v,a
else return v,v-c,x+v-c,a
end
end
local function RGBToHSV(r,g,b,a)
local max=math.max(r,g,b) -- = value
local min=math.min(r,g,b)
local chroma=max-min
local hue=(
max==min and 0 or
max==r and (g-b)/chroma%6/6 or
max==g and (2+(b-r)/chroma)/6 or
(4+(r-g)/chroma)/6
)
local saturation=max==0 and 0 or chroma/max
return hue,saturation,max,a
end
local COLOR={
hsv=HSVToRGB, -- backwards compatibility
HSVToRGB=HSVToRGB,
RGBToHSV=RGBToHSV,
red= {HSVToRGB(0.00, 0.89, 0.91)},
fire= {HSVToRGB(0.04, 0.93, 0.94)},
orange= {HSVToRGB(0.09, 0.99, 0.96)},
yellow= {HSVToRGB(0.15, 0.82, 0.90)},
lime= {HSVToRGB(0.20, 0.89, 0.88)},
jade= {HSVToRGB(0.25, 1.00, 0.82)},
green= {HSVToRGB(0.33, 1.00, 0.81)},
aqua= {HSVToRGB(0.47, 1.00, 0.76)},
cyan= {HSVToRGB(0.53, 1.00, 0.88)},
navy= {HSVToRGB(0.56, 1.00, 1.00)},
sea= {HSVToRGB(0.61, 1.00, 1.00)},
blue= {HSVToRGB(0.64, 1.00, 0.95)},
violet= {HSVToRGB(0.74, 1.00, 0.91)},
purple= {HSVToRGB(0.80, 1.00, 0.81)},
magenta= {HSVToRGB(0.86, 1.00, 0.78)},
wine= {HSVToRGB(0.92, 0.98, 0.91)},
lRed= {HSVToRGB(0.00, 0.38, 0.93)},
lFire= {HSVToRGB(0.04, 0.45, 0.91)},
lOrange= {HSVToRGB(0.10, 0.53, 0.92)},
lYellow= {HSVToRGB(0.14, 0.61, 0.95)},
lLime= {HSVToRGB(0.20, 0.66, 0.92)},
lJade= {HSVToRGB(0.26, 0.56, 0.90)},
lGreen= {HSVToRGB(0.34, 0.49, 0.89)},
lAqua= {HSVToRGB(0.47, 0.59, 0.86)},
lCyan= {HSVToRGB(0.51, 0.77, 0.88)},
lNavy= {HSVToRGB(0.54, 0.80, 0.95)},
lSea= {HSVToRGB(0.57, 0.72, 0.97)},
lBlue= {HSVToRGB(0.64, 0.44, 0.96)},
lViolet= {HSVToRGB(0.72, 0.47, 0.95)},
lPurple= {HSVToRGB(0.80, 0.62, 0.89)},
lMagenta= {HSVToRGB(0.86, 0.61, 0.89)},
lWine= {HSVToRGB(0.93, 0.57, 0.92)},
dRed= {HSVToRGB(0.00, 0.80, 0.48)},
dFire= {HSVToRGB(0.04, 0.80, 0.34)},
dOrange= {HSVToRGB(0.07, 0.80, 0.39)},
dYellow= {HSVToRGB(0.12, 0.80, 0.37)},
dLime= {HSVToRGB(0.20, 0.80, 0.26)},
dJade= {HSVToRGB(0.29, 0.80, 0.27)},
dGreen= {HSVToRGB(0.33, 0.80, 0.26)},
dAqua= {HSVToRGB(0.46, 0.80, 0.24)},
dCyan= {HSVToRGB(0.50, 0.80, 0.30)},
dNavy= {HSVToRGB(0.58, 0.80, 0.42)},
dSea= {HSVToRGB(0.64, 0.80, 0.40)},
dBlue= {HSVToRGB(0.67, 0.80, 0.34)},
dViolet= {HSVToRGB(0.71, 0.80, 0.35)},
dPurple= {HSVToRGB(0.76, 0.80, 0.32)},
dMagenta= {HSVToRGB(0.87, 0.80, 0.28)},
dWine= {HSVToRGB(0.92, 0.80, 0.28)},
black= {HSVToRGB(0.04, 0.04, 0.14)},
dGray= {HSVToRGB(0.02, 0.05, 0.44)},
gray= {HSVToRGB(0.02, 0.05, 0.65)},
lGray= {HSVToRGB(0.02, 0.06, 0.86)},
white= {HSVToRGB(0.01, 0.02, 0.99)},
xGray= {HSVToRGB(0.00, 0.00, 0.35,.8)},
lxGray= {HSVToRGB(0.00, 0.00, 0.62,.8)},
dxGray= {HSVToRGB(0.00, 0.00, 0.16,.8)},
}
for k,v in next,{
R='red', F='fire', O='orange', Y='yellow', L='lime', J='jade', G='green', A='aqua', C='cyan', N='navy', S='sea', B='blue', V='violet', P='purple', M='magenta', W='wine',
lR='lRed',lF='lFire',lO='lOrange',lY='lYellow',lL='lLime',lJ='lJade',lG='lGreen',lA='lAqua',lC='lCyan',lN='lNavy',lS='lSea',lB='lBlue',lV='lViolet',lP='lPurple',lM='lMagenta',lW='lWine',
dR='dRed',dF='dFire',dO='dOrange',dY='dYellow',dL='dLime',dJ='dJade',dG='dGreen',dA='dAqua',dC='dCyan',dN='dNavy',dS='dSea',dB='dBlue',dV='dViolet',dP='dPurple',dM='dMagenta',dW='dWine',
D='black',dH='dGray',H='gray',lH='lGray',Z='white',
X='xGray',lX='lxGray',dX='dxGray',
-- Remain letter: EIKQTU
} do
COLOR[k]=COLOR[v]
end
setmetatable(COLOR,{__index=function(_,k)
error("No color: "..tostring(k))
end})
do-- Random generators
local rnd=math.random
local list_norm={'red','fire','orange','yellow','lime','jade','green','aqua','cyan','navy','sea','blue','violet','purple','magenta','wine'}
local len_list_norm=#list_norm
function COLOR.random_norm()
return COLOR[list_norm[rnd(len_list_norm)]]
end
local list_bright={'lRed','lFire','lOrange','lYellow','lLime','lJade','lGreen','lAqua','lCyan','lNavy','lSea','lBlue','lViolet','lPurple','lMagenta','lWine'}
local len_list_bright=#list_bright
function COLOR.random_bright()
return COLOR[list_bright[rnd(len_list_bright)]]
end
local list_dark={'dRed','dFire','dOrange','dYellow','dLime','dJade','dGreen','dAqua','dCyan','dNavy','dSea','dBlue','dViolet','dPurple','dMagenta','dWine'}
local len_list_dark=#list_dark
function COLOR.random_dark()
return COLOR[list_dark[rnd(len_list_dark)]]
end
end
do-- Rainbow generators
local twoThirdsOfPi=2*math.pi/3
local sin=math.sin
function COLOR.rainbow(phase,alpha)
return
sin(phase)*.4+.6,
sin(phase+twoThirdsOfPi)*.4+.6,
sin(phase-twoThirdsOfPi)*.4+.6,
alpha
end
function COLOR.rainbow_light(phase,alpha)
return
sin(phase)*.2+.7,
sin(phase+twoThirdsOfPi)*.2+.7,
sin(phase-twoThirdsOfPi)*.2+.7,
alpha
end
function COLOR.rainbow_dark(phase,alpha)
return
sin(phase)*.2+.4,
sin(phase+twoThirdsOfPi)*.2+.4,
sin(phase-twoThirdsOfPi)*.2+.4,
alpha
end
function COLOR.rainbow_gray(phase,alpha)
return
sin(phase)*.16+.5,
sin(phase+twoThirdsOfPi)*.16+.5,
sin(phase-twoThirdsOfPi)*.16+.5,
alpha
end
end
do -- Color interpolation https://www.alanzucconi.com/2016/01/06/colour-interpolation/
local lerp=MATH.mix
function COLOR.interpolateRGB(rgba1,rgba2,t)
return
lerp(rgba1[1],rgba2[1],t),
lerp(rgba1[2],rgba2[2],t),
lerp(rgba1[3],rgba2[3],t),
((rgba1[4] and rgba2[4]) and lerp(rgba1[4],rgba1[4],t) or nil)
end
function COLOR.interpolateHSV(hsv1,hsv2,t)
local hue1,hue2=hsv1[1],hsv2[1]
if hue1>hue2 then
hue1,hue2=hue2,hue1
t=1-t
end
local hueDiff=hue2-hue1
local finalHue=.0
if hueDiff>.5 then
hue1=hue1+1
finalHue=(hue1+t*(hue2-hue1))%1;
else
finalHue=hue1+t*hueDiff
end
return finalHue,
lerp(hsv1[2],hsv2[2],t),
lerp(hsv1[3],hsv2[3],t),
((hsv1[4] and hsv2[4]) and lerp(hsv1[4],hsv1[4],t) or nil)
end
end
--[[
Returns either color1 or color2 based on time.
Args:
- color1, color2: colors to switch between
- period: the length of time in a cycle in seconds. ("wavelength")
- percentage [optional]: percentage of time in the color1 phase (default: 50%)
]]
function COLOR.flicker(color1,color2,period,percentage)
percentage=percentage or .5
return TIME()%period>percentage*period and color1 or color2
end
local lerpRGB,lerpHSV=COLOR.interpolateRGB,COLOR.interpolateHSV
local tau=MATH.tau
--[[
Oscillates between color1 and color2 over time in a sinusoidal fashion.
Args:
- color1, color2: colors to switch between.
[FORMAT IS BASED ON TYPE, WHICH DEFAULTS TO HSV]
Use COLOR.HSVtoRGB() and/or COLOR.RGBtoHSV to convert between color formats.
- period: the length of time in a cycle in seconds. (wavelength)
- type [optional]: the type of interpolation, defaulting to 'hsv'.
Supported values: nil, 'hsv', 'rgb'
]]
function COLOR.sine(color1,color2,period,type)
type=type or 'hsv'
local t=math.sin(tau*TIME()/period)
if type=='hsv' then
return lerpHSV(color1, color2, t)
elseif type=='rgb' then
return lerpRGB(color1, color2, t)
end
end
return COLOR

112
Zframework/file.lua Normal file
View File

@@ -0,0 +1,112 @@
local fs=love.filesystem
local FILE={}
function FILE.isSafe(file)
return SYSTEM=='Web' or fs.getRealDirectory(file)~=fs.getSaveDirectory()
end
function FILE.load(name,args)
if not args then args='' end
if fs.getInfo(name) then
local F=fs.newFile(name)
assert(F:open'r','open error')
local s=F:read() F:close()
local mode=
STRING.sArg(args,'-luaon') and 'luaon' or
STRING.sArg(args,'-lua') and 'lua' or
STRING.sArg(args,'-json') and 'json' or
STRING.sArg(args,'-string') and 'string' or
s:sub(1,9):find('return%s*%{') and 'luaon' or
(s:sub(1,1)=='[' and s:sub(-1)==']' or s:sub(1,1)=='{' and s:sub(-1)=='}') and 'json' or
'string'
if mode=='luaon' then
local func,err_mes=loadstring(s)
if func then
setfenv(func,{})
local res=func()
return assert(res,'decode error')
else
error('decode error: '..err_mes)
end
elseif mode=='lua' then
local func,err_mes=loadstring(s)
if func then
local res=func()
return assert(res,'run error')
else
error('compile error: '..err_mes)
end
elseif mode=='json' then
local res=JSON.decode(s)
if res then
return res
end
error('decode error')
elseif mode=='string' then
return s
else
error('unknown mode')
end
elseif not STRING.sArg(args,'-canskip') then
error('no file')
end
end
function FILE.save(data,name,args)
if not args then args='' end
if STRING.sArg(args,'-d') and fs.getInfo(name) then
error('duplicate')
end
if type(data)=='table' then
if STRING.sArg(args,'-luaon') then
if STRING.sArg(args,'-expand') then
data=TABLE.dump(data)
else
data='return'..TABLE.dumpDeflate(data)
end
if not data then
error('encode error')
end
else
data=JSON.encode(data)
if not data then
error('encode error')
end
end
else
data=tostring(data)
end
local F=fs.newFile(name)
assert(F:open('w'),'open error')
F:write(data) F:flush() F:close()
end
function FILE.clear(path)
if not FILE.isSafe(path) and fs.getInfo(path).type=='directory' then
for _,name in next,fs.getDirectoryItems(path) do
name=path..'/'..name
if not FILE.isSafe(name) then
local t=fs.getInfo(name).type
if t=='file' then
fs.remove(name)
end
end
end
end
end
function FILE.clear_s(path)
if path=='' or (not FILE.isSafe(path) and fs.getInfo(path).type=='directory') then
for _,name in next,fs.getDirectoryItems(path) do
name=path..'/'..name
if not FILE.isSafe(name) then
local t=fs.getInfo(name).type
if t=='file' then
fs.remove(name)
elseif t=='directory' then
FILE.clear_s(name)
fs.remove(name)
end
end
end
fs.remove(path)
end
end
return FILE

59
Zframework/font.lua Normal file
View File

@@ -0,0 +1,59 @@
local set=GC.setFont
local fontFiles,fontCache={},{}
local defaultFont,defaultFallBack
local curFont=false-- Current using font object
local FONT={}
function FONT.setDefault(name) defaultFont=name end
function FONT.setFallback(name) defaultFallBack=name end
function FONT.rawget(s)
if not fontCache[s] then
fontCache[s]=GC.setNewFont(s,'light',GC.getDPIScale()*SCR.k*2)
end
return fontCache[s]
end
function FONT.rawset(s)
set(fontCache[s] or FONT.rawget(s))
end
function FONT.load(fonts)
for name,path in next,fonts do
assert(love.filesystem.getInfo(path),STRING.repD("Font file $1($2) not exist!",name,path))
fontFiles[name]=love.filesystem.newFile(path)
fontCache[name]={}
end
FONT.reset()
end
function FONT.get(size,name)
if not name then name=defaultFont end
local f=fontCache[name][size]
if not f then
f=GC.setNewFont(fontFiles[name],size,'light',GC.getDPIScale()*SCR.k*2)
if defaultFallBack and name~=defaultFallBack then
f:setFallbacks(FONT.get(size,defaultFallBack))
end
fontCache[name][size]=f
end
return f
end
function FONT.set(size,name)
if not name then name=defaultFont end
local f=fontCache[name][size]
if f~=curFont then
curFont=f or FONT.get(size,name)
set(curFont)
end
end
function FONT.reset()
for name,cache in next,fontCache do
if type(cache)=='table' then
for size in next,cache do
cache[size]=FONT.get(size,name)
end
else
fontCache[name]=FONT.rawget(name)
end
end
end
return FONT

184
Zframework/gcExtend.lua Normal file
View File

@@ -0,0 +1,184 @@
local gc=love.graphics
local setColor,printf,draw=gc.setColor,gc.printf,gc.draw
local sin,cos=math.sin,math.cos
local GC=setmetatable({},{
__index=gc,
__metatable=true
})
function GC.mStr(obj,x,y) printf(obj,x-626,y,1252,'center') end-- Printf a string with 'center'
function GC.simpX(obj,x,y) draw(obj,x-obj:getWidth()*.5,y) end-- Simply draw an obj with x=obj:getWidth()/2
function GC.simpY(obj,x,y) draw(obj,x,y-obj:getHeight()*.5) end-- Simply draw an obj with y=obj:getWidth()/2
function GC.X(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,0) end-- Draw an obj with x=obj:getWidth()/2
function GC.Y(obj,x,y,a,k) draw(obj,x,y,a,k,nil,0,obj:getHeight()*.5) end-- Draw an obj with y=obj:getWidth()/2
function GC.mDraw(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,obj:getHeight()*.5) end-- Draw an obj with both middle X & Y
function GC.outDraw(obj,div,x,y,a,k)
local w,h=obj:getWidth()*.5,obj:getHeight()*.5
draw(obj,x-div,y-div,a,k,nil,w,h)
draw(obj,x-div,y+div,a,k,nil,w,h)
draw(obj,x+div,y-div,a,k,nil,w,h)
draw(obj,x+div,y+div,a,k,nil,w,h)
end
function GC.shadedPrint(str,x,y,mode,d,clr1,clr2)
local w=1280
if mode=='center' then
x=x-w*.5
elseif mode=='right' then
x=x-w
end
if not d then d=1 end
setColor(clr1 or COLOR.D)
printf(str,x-d,y-d,w,mode)
printf(str,x-d,y+d,w,mode)
printf(str,x+d,y-d,w,mode)
printf(str,x+d,y+d,w,mode)
setColor(clr2 or COLOR.Z)
printf(str,x,y,w,mode)
end
function GC.regPolygon(mode,x,y,R,segments,phase)
local l={}
local ang=phase or 0
local angStep=6.283185307179586/segments
for i=1,segments do
l[2*i-1]=x+R*cos(ang)
l[2*i]=y+R*sin(ang)
ang=ang+angStep
end
gc.polygon(mode,l)
end
function GC.regRoundPolygon(mode,x,y,R,segments,r,phase)
local X,Y={},{}
local ang=phase or 0
local angStep=6.283185307179586/segments
for i=1,segments do
X[i]=x+R*cos(ang)
Y[i]=y+R*sin(ang)
ang=ang+angStep
end
X[segments+1]=x+R*cos(ang)
Y[segments+1]=y+R*sin(ang)
local halfAng=6.283185307179586/segments/2
local erasedLen=r*math.tan(halfAng)
if mode=='line' then
erasedLen=erasedLen+1-- Fix 1px cover
for i=1,segments do
-- Line
local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1]
local dir=math.atan2(y2-y1,x2-x1)
gc.line(x1+erasedLen*cos(dir),y1+erasedLen*sin(dir),x2-erasedLen*cos(dir),y2-erasedLen*sin(dir))
-- Arc
ang=ang+angStep
local R2=R-r/cos(halfAng)
local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang)
gc.arc('line','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng)
end
elseif mode=='fill' then
local L={}
for i=1,segments do
-- Line
local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1]
local dir=math.atan2(y2-y1,x2-x1)
L[4*i-3]=x1+erasedLen*cos(dir)
L[4*i-2]=y1+erasedLen*sin(dir)
L[4*i-1]=x2-erasedLen*cos(dir)
L[4*i]=y2-erasedLen*sin(dir)
-- Arc
ang=ang+angStep
local R2=R-r/cos(halfAng)
local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang)
gc.arc('fill','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng)
end
gc.polygon('fill',L)
else
error("Draw mode should be 'line' or 'fill'")
end
end
do-- function GC.DO(L)
local cmds={
origin="origin",
move="translate",
scale="scale",
rotate="rotate",
shear="shear",
clear="clear",
setCL="setColor",
setCM="setColorMask",
setLW="setLineWidth",
setLS="setLineStyle",
setLJ="setLineJoin",
setBM="setBlendMode",
print="print",
rawFT=function(...)FONT.rawset(...) end,
setFT=function(...)FONT.set(...) end,
mText=GC.mStr,
mDraw=GC.mDraw,
mDrawX=GC.X,
mDrawY=GC.Y,
mOutDraw=GC.outDraw,
draw="draw",
line="line",
fRect=function(...)gc.rectangle('fill',...) end,
dRect=function(...)gc.rectangle('line',...) end,
fCirc=function(...)gc.circle('fill',...) end,
dCirc=function(...)gc.circle('line',...) end,
fElps=function(...)gc.ellipse('fill',...) end,
dElps=function(...)gc.ellipse('line',...) end,
fPoly=function(...)gc.polygon('fill',...) end,
dPoly=function(...)gc.polygon('line',...) end,
dPie=function(...)gc.arc('line',...) end,
dArc=function(...)gc.arc('line','open',...) end,
dBow=function(...)gc.arc('line','closed',...) end,
fPie=function(...)gc.arc('fill',...) end,
fArc=function(...)gc.arc('fill','open',...) end,
fBow=function(...)gc.arc('fill','closed',...) end,
fRPol=function(...)GC.regPolygon('fill',...) end,
dRPol=function(...)GC.regPolygon('line',...) end,
fRRPol=function(...)GC.regRoundPolygon('fill',...) end,
dRRPol=function(...)GC.regRoundPolygon('line',...) end,
}
local sizeLimit=gc.getSystemLimits().texturesize
function GC.DO(L)
gc.push()
local success,canvas
repeat
success,canvas=pcall(gc.newCanvas,math.min(L[1],sizeLimit),math.min(L[2],sizeLimit))
if not success then
sizeLimit=math.floor(sizeLimit*.8)
end
until success
gc.setCanvas(canvas)
gc.origin()
gc.clear(1,1,1,0)
gc.setColor(1,1,1)
gc.setLineWidth(1)
for i=3,#L do
local cmd=L[i][1]
if type(cmd)=='boolean' and cmd then
table.remove(L[i],1)
cmd=L[i][1]
end
if type(cmd)=='string' then
local func=cmds[cmd] or gc[cmd]
if type(func)=='string' then
func=gc[func]
end
if func then
func(unpack(L[i],2))
else
error("No gc command: "..cmd)
end
end
end
gc.setCanvas()
gc.pop()
return canvas
end
end
return GC

191
Zframework/http.lua Normal file
View File

@@ -0,0 +1,191 @@
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
local threads={}
local threadCount=0
local threadCode=[[
local id=...
local http=require'socket.http'
local ltn12=require'ltn12'
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
while true do
local arg=sendCHN:demand()
if arg._destroy then
recvCHN:push{
destroy=true,
id=id,
}
break
end
-- print("\n------SEND------") for k,v in next,arg do print(k,v) end
local data={}
local _,code,detail=http.request{
method=arg.method,
url=arg.url,
headers=arg.headers,
source=ltn12.source.string(arg.body),
sink=ltn12.sink.table(data),
}
local result={
pool=arg.pool,
poolPtr=arg.poolPtr,
code=code,
body=table.concat(data),
detail=detail,
}
-- print("\n------RECV------") for k,v in next,result do print(k,v) end
recvCHN:push(result)
end
]]
local msgPool=setmetatable({},{
__index=function(self,k)
self[k]={}
return self[k]
end
})
local HTTP={
_msgCount=0,
_trigTime=0,
_trigInterval=.26,
_host=false,
}
local function addThread(num)
for i=1,26 do
if num<=0 then break end
if not threads[i] then
threads[i]=love.thread.newThread(threadCode)
threads[i]:start(i)
threadCount=threadCount+1
num=num-1
end
end
end
function HTTP.request(arg)
arg.method=arg.method or arg.body and 'POST' or 'GET'
if arg.url then
assert(type(arg.url)=='string',"Field 'url' need string, get "..type(arg.url))
if arg.url:sub(1,7)~='http://' then arg.url='http://'..arg.url end
else
arg.url=HTTP._host or error("Need url=<string> or set default host with HTTP.setHost")
end
if arg.path then
assert(type(arg.path)=='string',"Field 'path' need string, get "..type(arg.path))
arg.url=arg.url..arg.path
end
assert(arg.headers==nil or type(arg.headers)=='table',"Field 'headers' need table, get "..type(arg.headers))
if arg.body~=nil then
assert(type(arg.body)=='table',"Field 'body' need table, get "..type(arg.body))
arg.body=JSON.encode(arg.body)
if not arg.headers then arg.headers={} end
TABLE.cover({
['Content-Type']="application/json",
['Content-Length']=#arg.body,
},arg.headers)
end
if arg.pool==nil then arg.pool='_default' end
arg.poolPtr=tostring(msgPool[arg.pool])
sendCHN:push(arg)
end
function HTTP.reset()
for i=1,#threads do
threads[i]:release()
threads[i]=false
end
TABLE.clear(msgPool)
sendCHN:clear()
recvCHN:clear()
addThread(threadCount)
end
function HTTP.setThreadCount(n)
assert(type(n)=='number' and n>=1 and n<=26 and n%1==0,"function HTTP.setThreadCount(n): n must be integer from 1 to 26")
if n>threadCount then
addThread(n-threadCount)
else
for _=n+1,threadCount do
sendCHN:push{_destroy=true}
end
end
end
function HTTP.getThreadCount()
return threadCount
end
function HTTP.setInterval(interval)
if interval<=0 then interval=1e99 end
assert(type(interval)=='number',"Interval must be number")
HTTP._trigInterval=interval
end
function HTTP.clearPool(pool)
if pool==nil then pool='_default' end
assert(type(pool)=='string',"Pool must be nil or string")
HTTP._msgCount=HTTP._msgCount-#msgPool[pool]
msgPool[pool]={}
end
function HTTP.deletePool(pool)
assert(type(pool)=='string',"Pool must be nil or string")
assert(pool~='_default',"Cannot delete _default pool. What are you doing?")
HTTP._msgCount=HTTP._msgCount-#msgPool[pool]
msgPool[pool]=nil
end
function HTTP.pollMsg(pool)
if not (type(pool)=='nil' or type(pool)=='string') then error("Pool must be nil or string") end
HTTP.update()
local p=msgPool[pool or '_default']
if #p>0 then
HTTP._msgCount=HTTP._msgCount-1
return table.remove(p)
end
end
function HTTP.setHost(host)
assert(type(host)=='string',"Host must be string")
if host:sub(1,7)~='http://' then host='http://'..host end
HTTP._host=host
end
function HTTP.update(dt)
if dt then
HTTP._trigTime=HTTP._trigTime+dt
if HTTP._trigTime>HTTP._trigInterval then
HTTP._trigTime=HTTP._trigTime%HTTP._trigInterval
else
return
end
end
while recvCHN:getCount()>0 do
local m=recvCHN:pop()
if m.destroy then
threads[m.id]:release()
threads[m.id]=false
elseif tostring(msgPool[m.pool])==m.poolPtr then -- If pool were cleared, discard this datapack
table.insert(msgPool[m.pool],{
code=m.code,
body=m.body,
detail=m.detail,
})
HTTP._msgCount=HTTP._msgCount+1
end
end
end
setmetatable(HTTP,{__call=function(self,arg)
return self.request(arg)
end,__metatable=true})
HTTP.reset()
return HTTP

25
Zframework/image.lua Normal file
View File

@@ -0,0 +1,25 @@
local IMG={}
function IMG.init(list)
IMG.init=nil
setmetatable(IMG,{__index=function(self,name)
if type(list[name])=='table' then
self[name]={}
for k,v in next,list[name] do
self[name][k]=love.graphics.newImage(v)
end
elseif type(list[name])=='string' then
self[name]=love.graphics.newImage(list[name])
else
LOG("No IMG: "..name)
self[name]=PAPER
end
return self[name]
end})
function IMG.loadAll()
for k in next,list do local _=IMG[k] end
IMG.loadAll=nil
end
end
return IMG

932
Zframework/init.lua Normal file
View File

@@ -0,0 +1,932 @@
NONE={}function NULL() end PAPER=love.graphics.newCanvas(1,1)
EDITING=""
LOADED=false
SYSTEM=love.system.getOS()
if SYSTEM=='OS X' then SYSTEM='macOS' end
-- Bit module
local success
success,bit=pcall(require,"bit")
if not success then
bit=require"Zframework.bitop".bit
end
-- Pure lua modules (basic)
MATH= require'Zframework.mathExtend'
COLOR= require'Zframework.color'
TABLE= require'Zframework.tableExtend'
STRING= require'Zframework.stringExtend'
PROFILE= require'Zframework.profile'
JSON= require'Zframework.json'
TEST= require'Zframework.test'
do-- Add pcall & MES for JSON lib
local encode,decode=JSON.encode,JSON.decode
JSON.encode=function(val)
local a,b=pcall(encode,val)
if a then
return b
elseif MES then
MES.traceback()
end
end
JSON.decode=function(str)
local a,b=pcall(decode,str)
if a then
return b
elseif MES then
MES.traceback()
end
end
end
-- Pure lua modules (complex)
LOG= require'Zframework.log'
REQUIRE= require'Zframework.require'
TASK= require'Zframework.task'
LANG= require'Zframework.languages'
HASH= require'Zframework.sha2'
do
local bxor=bit.bxor
local char=string.char
local function sxor(s1, s2)
local b3=""
for i=1,#s1 do
b3=b3..char(bxor(s1:byte(i),s2:byte(i)))
end
return b3
end
function HASH.pbkdf2(hashFunc, pw, salt, n)
local u=HASH.hex2bin(HASH.hmac(hashFunc, pw, salt.."\0\0\0\1"))
local t=u
for _=2,n do
u=HASH.hex2bin(HASH.hmac(hashFunc, pw, u))
t=sxor(t, u)
end
return HASH.bin2hex(t):upper()
end
end
-- Love-based modules (basic)
HTTP= require'Zframework.http'
WS= require'Zframework.websocket'
FILE= require'Zframework.file'
WHEELMOV= require'Zframework.wheelScroll'
SCR= require'Zframework.screen'
SCN= require'Zframework.scene'
-- Love-based modules (complex)
GC= require'Zframework.gcExtend'
FONT= require'Zframework.font'
TEXT= require'Zframework.text'
SYSFX= require'Zframework.sysFX'
WAIT= require'Zframework.wait'
MES= require'Zframework.message'
BG= require'Zframework.background'
WIDGET= require'Zframework.widget'
VIB= require'Zframework.vibrate'
SFX= require'Zframework.sfx'
IMG= require'Zframework.image'
BGM= require'Zframework.bgm'
VOC= require'Zframework.voice'
local ms,kb=love.mouse,love.keyboard
local KBisDown=kb.isDown
local gc=love.graphics
local gc_push,gc_pop,gc_clear,gc_discard=gc.push,gc.pop,gc.clear,gc.discard
local gc_replaceTransform,gc_present=gc.replaceTransform,gc.present
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
local gc_draw,gc_line,gc_circle,gc_print=gc.draw,gc.line,gc.circle,gc.print
local BG,WIDGET,SCR,SCN,WAIT=BG,WIDGET,SCR,SCN,WAIT
local xOy=SCR.xOy
local ITP=xOy.inverseTransformPoint
local max,min=math.max,math.min
local debugMode
local mx,my,mouseShow,cursorSpd=640,360,false,0
local jsState={}-- map, joystickID->axisStates: {axisName->axisVal}
local errData={}-- list, each error create {mes={errMes strings},scene=sceneNameStr}
local function drawCursor(_,x,y)
gc_setColor(1,1,1)
gc_setLineWidth(2)
gc_circle(ms.isDown(1) and 'fill' or 'line',x,y,6)
end
local showPowerInfo=true
local showClickFX=true
local discardCanvas=false
local frameMul=100
local sleepInterval=1/60
local onQuit=NULL
local onBeforeQuit=false
local versionText=""
local batteryImg=GC.DO{31,20,
{'fRect',1,0,26,2},
{'fRect',1,18,26,2},
{'fRect',0,1,2,18},
{'fRect',26,1,2,18},
{'fRect',29,3,2,14},
}
local infoCanvas=gc.newCanvas(108,27)
local function updatePowerInfo()
local state,pow=love.system.getPowerInfo()
gc.setCanvas(infoCanvas)
gc_push('transform')
gc.origin()
gc_clear(0,0,0,.25)
if state~='unknown' then
gc_setLineWidth(4)
if state=='nobattery' then
gc_setColor(1,1,1)
gc_setLineWidth(2)
gc_line(74,5,100,22)
elseif pow then
if state=='charging' then gc_setColor(0,1,0)
elseif pow>50 then gc_setColor(1,1,1)
elseif pow>26 then gc_setColor(1,1,0)
elseif pow==26 then gc_setColor(.5,0,1)
else gc_setColor(1,0,0)
end
gc.rectangle('fill',76,6,pow*.22,14)
if pow<100 then
FONT.set(15)
gc.setColor(COLOR.D)
gc_print(pow,77,1)
gc_print(pow,77,3)
gc_print(pow,79,1)
gc_print(pow,79,3)
gc_setColor(COLOR.Z)
gc_print(pow,78,2)
end
end
gc_draw(batteryImg,73,3)
end
FONT.set(25)
gc_print(os.date("%H:%M"),3,-5)
gc_pop()
gc.setCanvas()
end
-------------------------------------------------------------
local lastX,lastY=0,0-- Last click pos
local function _updateMousePos(x,y,dx,dy)
if SCN.swapping or WAIT.state then return end
dx,dy=dx/SCR.k,dy/SCR.k
if SCN.mouseMove then SCN.mouseMove(x,y,dx,dy) end
if ms.isDown(1) then
WIDGET.drag(x,y,dx,dy)
else
WIDGET.cursorMove(x,y)
end
end
local function mouse_update(dt)
if not KBisDown('lctrl','rctrl') and KBisDown('up','down','left','right') then
local dx,dy=0,0
if KBisDown('up') then dy=dy-cursorSpd end
if KBisDown('down') then dy=dy+cursorSpd end
if KBisDown('left') then dx=dx-cursorSpd end
if KBisDown('right') then dx=dx+cursorSpd end
mx=max(min(mx+dx,1280),0)
my=max(min(my+dy,720),0)
if my==0 or my==720 then
WIDGET.sel=false
WIDGET.drag(0,0,0,-dy)
end
_updateMousePos(mx,my,dx,dy)
cursorSpd=min(cursorSpd+dt*26,12.6)
else
cursorSpd=6
end
end
local function gp_update(js,dt)
local sx,sy=js._jsObj:getGamepadAxis('leftx'),js._jsObj:getGamepadAxis('lefty')
if math.abs(sx)>.1 or math.abs(sy)>.1 then
local dx,dy=0,0
if sy<-.1 then dy=dy+2*sy*cursorSpd end
if sy>.1 then dy=dy+2*sy*cursorSpd end
if sx<-.1 then dx=dx+2*sx*cursorSpd end
if sx>.1 then dx=dx+2*sx*cursorSpd end
mx=max(min(mx+dx,1280),0)
my=max(min(my+dy,720),0)
if my==0 or my==720 then
WIDGET.sel=false
WIDGET.drag(0,0,0,-dy)
end
_updateMousePos(mx,my,dx,dy)
cursorSpd=min(cursorSpd+dt*26,12.6)
else
cursorSpd=6
end
end
function love.mousepressed(x,y,k,touch)
if touch or WAIT.state then return end
mouseShow=true
mx,my=ITP(xOy,x,y)
if debugMode==1 then
print(("(%d,%d)<-%d,%d ~~(%d,%d)<-%d,%d"):format(
mx,my,
mx-lastX,my-lastY,
math.floor(mx/10)*10,math.floor(my/10)*10,
math.floor((mx-lastX)/10)*10,math.floor((my-lastY)/10)*10
))
end
if SCN.swapping then return end
if SCN.mouseDown then SCN.mouseDown(mx,my,k) end
WIDGET.press(mx,my,k)
lastX,lastY=mx,my
if showClickFX then SYSFX.newTap(3,mx,my) end
end
function love.mousemoved(x,y,dx,dy,touch)
if touch then return end
mouseShow=true
mx,my=ITP(xOy,x,y)
_updateMousePos(mx,my,dx,dy)
end
function love.mousereleased(x,y,k,touch)
if touch or WAIT.state or SCN.swapping then return end
mx,my=ITP(xOy,x,y)
if SCN.mouseUp then SCN.mouseUp(mx,my,k) end
if WIDGET.sel then
WIDGET.release(mx,my,k)
else
if lastX and SCN.mouseClick and (mx-lastX)^2+(my-lastY)^2<62 then
SCN.mouseClick(mx,my,k)
end
end
end
function love.wheelmoved(x,y)
if math.abs(x)>=100 then x=x/100 end
if math.abs(y)>=100 then y=y/100 end
if WAIT.state or SCN.swapping then return end
if SCN.wheelMoved then
SCN.wheelMoved(x,y)
else
WIDGET.unFocus()
WIDGET.drag(0,0,0,100*y)
end
end
function love.touchpressed(id,x,y)
mouseShow=false
if WAIT.state or SCN.swapping then return end
if not SCN.mainTouchID then
SCN.mainTouchID=id
WIDGET.unFocus(true)
love.touchmoved(id,x,y,0,0)
end
x,y=ITP(xOy,x,y)
lastX,lastY=x,y
if SCN.touchDown then SCN.touchDown(x,y,id) end
if kb.hasTextInput() then kb.setTextInput(false) end
WIDGET.cursorMove(x,y)
WIDGET.press(x,y,1)
end
function love.touchmoved(id,x,y,dx,dy)
if WAIT.state or SCN.swapping then return end
x,y=ITP(xOy,x,y)
if SCN.touchMove then SCN.touchMove(x,y,dx/SCR.k,dy/SCR.k,id) end
WIDGET.drag(x,y,dx/SCR.k,dy/SCR.k)
end
function love.touchreleased(id,x,y)
if WAIT.state or SCN.swapping then return end
x,y=ITP(xOy,x,y)
if id==SCN.mainTouchID then
WIDGET.release(x,y,1)
WIDGET.cursorMove(x,y)
WIDGET.unFocus()
SCN.mainTouchID=false
end
if SCN.touchUp then SCN.touchUp(x,y,id) end
if (x-lastX)^2+(y-lastY)^2<62 then
if SCN.touchClick then SCN.touchClick(x,y) end
if showClickFX then SYSFX.newTap(3,x,y) end
end
end
-- function love.mousepressed(x,y,k) love.touchpressed(1,x,y) end
-- function love.mousemoved(x,y,dx,dy,touch) love.touchmoved(1,x,y,dx,dy) end
-- function love.mousereleased(x,y,k) love.touchreleased(1,x,y) end
local globalKey={
f8=function()
debugMode=1
MES.new('info',"DEBUG ON",.2)
end
}
local fnKey={NULL,NULL,NULL,NULL,NULL,NULL,NULL}
local function debugKeyPressed(key)
if key=='f1' then fnKey[1]()
elseif key=='f2' then fnKey[2]()
elseif key=='f3' then fnKey[3]()
elseif key=='f4' then fnKey[4]()
elseif key=='f5' then fnKey[5]()
elseif key=='f6' then fnKey[6]()
elseif key=='f7' then fnKey[7]()
elseif key=='f8' then debugMode=nil MES.new('info',"DEBUG OFF",.2)
elseif key=='f9' then debugMode=1 MES.new('info',"DEBUG 1")
elseif key=='f10' then debugMode=2 MES.new('info',"DEBUG 2")
elseif key=='f11' then debugMode=3 MES.new('info',"DEBUG 3")
elseif key=='f12' then debugMode=4 MES.new('info',"DEBUG 4")
elseif debugMode==2 then
local W=WIDGET.sel
if W then
if key=='left' then W.x=W.x-10
elseif key=='right' then W.x=W.x+10
elseif key=='up' then W.y=W.y-10
elseif key=='down' then W.y=W.y+10
elseif key==',' then W.w=W.w-10
elseif key=='.' then W.w=W.w+10
elseif key=='/' then W.h=W.h-10
elseif key=='\'' then W.h=W.h+10
elseif key=='[' then W.font=W.font-5
elseif key==']' then W.font=W.font+5
else return
end
else
return
end
else
return
end
return true
end
function love.keypressed(key,_,isRep)
mouseShow=false
if debugMode and debugKeyPressed(key) then
-- Do nothing
elseif globalKey[key] then
globalKey[key]()
else
if SCN.swapping then return end
if WAIT.state then
if key=='escape' and WAIT.arg.escapable then WAIT.interrupt() end
return
end
if EDITING=="" and (not SCN.keyDown or SCN.keyDown(key,isRep)) then
local W=WIDGET.sel
if key=='escape' and not isRep then
SCN.back()
elseif key=='up' or key=='down' or key=='left' or key=='right' then
mouseShow=true
if KBisDown('lctrl','rctrl') then
if W and W.arrowKey then W:arrowKey(key) end
end
elseif key=='space' or key=='return' then
mouseShow=true
if not isRep then
if showClickFX then SYSFX.newTap(3,mx,my) end
love.mousepressed(mx,my,1)
love.mousereleased(mx,my,1)
end
else
if W and W.keypress then
W:keypress(key)
end
end
end
end
end
function love.keyreleased(i)
if WAIT.state or SCN.swapping then return end
if SCN.keyUp then SCN.keyUp(i) end
end
function love.textedited(texts)
EDITING=texts
end
function love.textinput(texts)
WIDGET.textinput(texts)
end
-- analog sticks: -1, 0, 1 for neg, neutral, pos
-- triggers: 0 for released, 1 for pressed
local jsAxisEventName={
leftx={'leftstick_left','leftstick_right'},
lefty={'leftstick_up','leftstick_down'},
rightx={'rightstick_left','rightstick_right'},
righty={'rightstick_up','rightstick_down'},
triggerleft='triggerleft',
triggerright='triggerright'
}
local gamePadKeys={'a','b','x','y','back','guide','start','leftstick','rightstick','leftshoulder','rightshoulder','dpup','dpdown','dpleft','dpright'}
local dPadToKey={
dpup='up',
dpdown='down',
dpleft='left',
dpright='right',
start='return',
back='escape',
}
function love.joystickadded(JS)
table.insert(jsState,{
_id=JS:getID(),
_jsObj=JS,
leftx=0,lefty=0,
rightx=0,righty=0,
triggerleft=0,triggerright=0
})
MES.new('info',"Joystick added")
end
function love.joystickremoved(JS)
for i=1,#jsState do
if jsState[i]._jsObj==JS then
for j=1,#gamePadKeys do
if JS:isGamepadDown(gamePadKeys[j]) then
love.gamepadreleased(JS,gamePadKeys[j])
end
end
love.gamepadaxis(JS,'leftx',0)
love.gamepadaxis(JS,'lefty',0)
love.gamepadaxis(JS,'rightx',0)
love.gamepadaxis(JS,'righty',0)
love.gamepadaxis(JS,'triggerleft',-1)
love.gamepadaxis(JS,'triggerright',-1)
MES.new('info',"Joystick removed")
table.remove(jsState,i)
break
end
end
end
function love.gamepadaxis(JS,axis,val)
if jsState[1] and JS==jsState[1]._jsObj then
local js=jsState[1]
if axis=='leftx' or axis=='lefty' or axis=='rightx' or axis=='righty' then
local newVal=-- range: [0,1]
val>.4 and 1 or
val<-.4 and -1 or
0
if newVal~=js[axis] then
if js[axis]==-1 then
love.gamepadreleased(JS,jsAxisEventName[axis][1])
elseif js[axis]~=0 then
love.gamepadreleased(JS,jsAxisEventName[axis][2])
end
if newVal==-1 then
love.gamepadpressed(JS,jsAxisEventName[axis][1])
elseif newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis][2])
end
js[axis]=newVal
end
elseif axis=='triggerleft' or axis=='triggerright' then
local newVal=val>.3 and 1 or 0-- range: [0,1]
if newVal~=js[axis] then
if newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis])
else
love.gamepadreleased(JS,jsAxisEventName[axis])
end
js[axis]=newVal
end
end
end
end
function love.gamepadpressed(_,key)
mouseShow=false
if not SCN.swapping then
local cursorCtrl
if SCN.gamepadDown then
cursorCtrl=SCN.gamepadDown(key)
elseif SCN.keyDown then
cursorCtrl=SCN.keyDown(dPadToKey[key] or key)
else
cursorCtrl=true
end
if cursorCtrl then
key=dPadToKey[key] or key
mouseShow=true
local W=WIDGET.sel
if key=='back' then
SCN.back()
elseif key=='up' or key=='down' or key=='left' or key=='right' then
mouseShow=true
if W and W.arrowKey then W:arrowKey(key) end
elseif key=='return' then
mouseShow=true
if showClickFX then SYSFX.newTap(3,mx,my) end
love.mousepressed(mx,my,1)
love.mousereleased(mx,my,1)
else
if W and W.keypress then
W:keypress(key)
end
end
end
end
end
function love.gamepadreleased(_,i)
if WAIT.state or SCN.swapping then return end
if SCN.gamepadUp then SCN.gamepadUp(i) end
end
function love.filedropped(file)
if WAIT.state or SCN.swapping then return end
if SCN.fileDropped then SCN.fileDropped(file) end
end
function love.directorydropped(dir)
if WAIT.state or SCN.swapping then return end
if SCN.directoryDropped then SCN.directoryDropped(dir) end
end
local autoGCcount=0
function love.lowmemory()
collectgarbage()
if autoGCcount<3 then
autoGCcount=autoGCcount+1
MES.new('check',"[auto GC] low MEM 设备内存过低")
end
end
local onResize=NULL
function love.resize(w,h)
if SCR.w==w and SCR.h==h then return end
SCR.resize(w,h)
if BG.resize then BG.resize(w,h) end
if SCN.resize then SCN.resize(w,h) end
WIDGET.resize(w,h)
FONT.reset()
onResize(w,h)
end
local onFocus=NULL
function love.focus(f) onFocus(f) end
local yield=coroutine.yield
local function secondLoopThread()
local mainLoop=love.run()
repeat yield() until mainLoop()
end
function love.errorhandler(msg)
if type(msg)~='string' then
msg="Unknown error"
elseif msg:find("Invalid UTF-8") and text then
msg=text.tryAnotherBuild
end
-- Generate error message
local err={"Error:"..msg}
local c=2
for l in debug.traceback("",2):gmatch("(.-)\n") do
if c>2 then
if not l:find("boot") then
err[c]=l:gsub("^\t*","")
c=c+1
end
else
err[2]="Traceback"
c=3
end
end
print(table.concat(err,"\n",1,c-2))
-- Reset something
love.audio.stop()
gc.reset()
local sceneStack=SCN and table.concat(SCN.stack,"/") or "NULL"
if LOADED and #errData<3 then
BG.set('none')
table.insert(errData,{mes=err,scene=sceneStack})
-- Write messages to log file
love.filesystem.append('conf/error.log',
os.date("%Y/%m/%d %A %H:%M:%S\n")..
#errData.." crash(es) "..SYSTEM.."-"..VERSION.string.." scene: "..sceneStack.."\n"..
table.concat(err,"\n",1,c-2).."\n\n"
)
-- Get screencapture
gc.captureScreenshot(function(_) errData[#errData].shot=gc.newImage(_) end)
gc.present()
-- Create a new mainLoop thread to keep game alive
local status,resume=coroutine.status,coroutine.resume
local loopThread=coroutine.create(secondLoopThread)
local res,threadErr
repeat
res,threadErr=resume(loopThread)
until status(loopThread)=='dead'
if not res then
love.errorhandler(threadErr)
return
end
else
ms.setVisible(true)
local errorMsg
errorMsg=LOADED and
"Too many errors or fatal error occured.\nPlease restart the game." or
"An error has occurred during loading.\nError info has been created, and you can send it to the author."
while true do
love.event.pump()
for E,a,b in love.event.poll() do
if E=='quit' or a=='escape' then
return true
elseif E=='resize' then
SCR.resize(a,b)
end
end
gc_clear(.3,.5,.9)
gc_push('transform')
gc_replaceTransform(SCR.xOy)
FONT.set(100)gc_print(":(",100,0,0,1.2)
FONT.set(40)gc.printf(errorMsg,100,160,SCR.w0-100)
FONT.set(20)
gc_print(SYSTEM.."-"..VERSION.string.." scene:"..sceneStack,100,660)
gc.printf(err[1],100,360,1260-100)
gc_print("TRACEBACK",100,450)
for i=4,#err-2 do
gc_print(err[i],100,400+20*i)
end
gc_pop()
gc_present()
love.timer.sleep(.26)
end
end
end
love.threaderror=nil
love.draw,love.update=nil-- remove default draw/update
local debugColor={
COLOR.Z,
COLOR.lM,
COLOR.lG,
COLOR.lB,
}
local debugInfos={
{"Cache",gcinfo},
}
function love.run()
local love=love
local TEXT_update,TEXT_draw=TEXT.update,TEXT.draw
local MES_update,MES_draw=MES.update,MES.draw
local HTTP_update,WS_update=HTTP.update,WS.update
local TASK_update=TASK.update
local SYSFX_update,SYSFX_draw=SYSFX.update,SYSFX.draw
local WIDGET_update,WIDGET_draw=WIDGET.update,WIDGET.draw
local STEP,SLEEP=love.timer.step,love.timer.sleep
local FPS,MINI=love.timer.getFPS,love.window.isMinimized
local PUMP,POLL=love.event.pump,love.event.poll
local timer=love.timer.getTime
local frameTimeList={}
local lastFrame=timer()
local lastFreshPow=lastFrame
local FCT=0-- Framedraw counter, from 0~99
love.resize(gc.getWidth(),gc.getHeight())
-- Scene Launch
while #SCN.stack>0 do SCN.pop() end
if #errData>0 then
SCN.cur='error'
SCN.init('error')
else
SCN.init('load')
end
return function()
local _
local time=timer()
local dt=time-lastFrame
lastFrame=time
-- EVENT
PUMP()
for N,a,b,c,d,e in POLL() do
if love[N] then
love[N](a,b,c,d,e)
elseif N=='quit' then
if onBeforeQuit then
onBeforeQuit()
onBeforeQuit=false
else
onQuit()
return a or true
end
end
end
-- UPDATE
STEP()
if mouseShow then mouse_update(dt) end
if next(jsState) then gp_update(jsState[1],dt) end
VOC.update()
BG.update(dt)
TEXT_update(dt)
WAIT.update(dt)
MES_update(dt)
HTTP_update(dt)
WS_update(dt)
TASK_update(dt)
SYSFX_update(dt)
if SCN.update then SCN.update(dt) end
if SCN.swapping then SCN.swapUpdate(dt) end
WIDGET_update(dt)
-- DRAW
if not MINI() then
FCT=FCT+frameMul
if FCT>=100 then
FCT=FCT-100
gc_replaceTransform(SCR.origin)
gc_setColor(1,1,1)
BG.draw()
gc_replaceTransform(SCR.xOy)
if SCN.draw then SCN.draw() end
WIDGET_draw()
SYSFX_draw()
TEXT_draw()
-- Draw cursor
if mouseShow then drawCursor(time,mx,my) end
gc_replaceTransform(SCR.xOy_ul)
if showPowerInfo then
gc.translate(0,27)
end
MES_draw()
gc_replaceTransform(SCR.origin)
-- Draw power info.
if showPowerInfo then
gc_setColor(1,1,1)
gc_draw(infoCanvas,SCR.safeX,0,0,SCR.k)
end
-- Draw scene swapping animation
if SCN.swapping then
gc_setColor(1,1,1)
_=SCN.state
_.draw(_.time)
end
gc_replaceTransform(SCR.xOy_d)
-- Draw Version string
gc_setColor(.9,.9,.9,.42)
FONT.set(20)
GC.mStr(versionText,0,-30)
gc_replaceTransform(SCR.xOy_dl)
local safeX=SCR.safeX/SCR.k
-- Draw FPS
FONT.set(15)
gc_setColor(1,1,1)
gc_print(FPS(),safeX+5,-20)
-- Debug info.
if debugMode then
-- Debug infos at left-down
gc_setColor(debugColor[debugMode])
-- Text infos
for i=1,#debugInfos do
gc_print(debugInfos[i][1],safeX+5,-20-20*i)
gc_print(debugInfos[i][2](),safeX+62.6,-20-20*i)
end
-- Update & draw frame time
table.insert(frameTimeList,1,dt)table.remove(frameTimeList,126)
gc_setColor(1,1,1,.3)
for i=1,#frameTimeList do
gc.rectangle('fill',150+2*i,-20,2,-frameTimeList[i]*4000)
end
-- Cursor pos disp
gc_replaceTransform(SCR.origin)
local x,y=SCR.xOy:transformPoint(mx,my)
gc_setLineWidth(1)
gc_line(x,0,x,SCR.h)
gc_line(0,y,SCR.w,y)
local t=math.floor(mx+.5)..","..math.floor(my+.5)
gc.setColor(COLOR.D)
gc_print(t,x+1,y)
gc_print(t,x+1,y-1)
gc_print(t,x+2,y-1)
gc_setColor(COLOR.Z)
gc_print(t,x+2,y)
gc_replaceTransform(SCR.xOy_dr)
-- Websocket status
local status=WS.status('game')
if status=='dead' then
gc_setColor(COLOR.R)
elseif status=='connecting' then
gc_setColor(1,1,1,.5+.3*math.sin(time*6.26))
elseif status=='running' then
gc_setColor(COLOR.lG)
end
gc.rectangle('fill',-16,-16,12,12)
local t1,t2,t3=WS.getTimers('game')
if t1>0 then gc_setColor(.9,.9,.9,t1)gc.rectangle('fill',-60,-2,-16,-16) end
if t2>0 then gc_setColor(.3,1,.3,t2)gc.rectangle('fill',-42,-2,-16,-16) end
if t3>0 then gc_setColor(1,.2,.2,t3)gc.rectangle('fill',-24,-2,-16,-16) end
end
gc_replaceTransform(SCR.origin)
WAIT.draw()
gc_present()
-- SPEED UPUPUP!
if discardCanvas then gc_discard() end
end
end
-- Fresh power info.
if time-lastFreshPow>2.6 then
if showPowerInfo then
updatePowerInfo()
lastFreshPow=time
end
if gc.getWidth()~=SCR.w or gc.getHeight()~=SCR.h then
love.resize(gc.getWidth(),gc.getHeight())
end
end
-- Slow debugmode
if debugMode then
if debugMode==3 then
SLEEP(.1)
elseif debugMode==4 then
SLEEP(.5)
end
end
_=timer()-lastFrame
if _<sleepInterval*.9626 then SLEEP(sleepInterval*.9626-_) end
while timer()-lastFrame<sleepInterval do end
end
end
local Z={}
function Z.getJsState() return jsState end
function Z.getErr(i)
if i=='#' then
return errData[#errData]
elseif i then
return errData[i]
else
return errData
end
end
function Z.setPowerInfo(bool) showPowerInfo=bool end
function Z.setCleanCanvas(bool) discardCanvas=bool end
function Z.setFrameMul(n) frameMul=n end
function Z.setMaxFPS(fps) sleepInterval=1/fps end
function Z.setClickFX(bool) showClickFX=bool end
--[Warning] Color and line width is uncertain value, set it in the function.
function Z.setCursor(func) drawCursor=func end
function Z.setVersionText(str) versionText=str end
function Z.setDebugInfo(list)
assert(type(list)=='table',"Z.setDebugInfo(list): list must be table")
for i=1,#list do
assert(type(list[i][1])=='string',"Z.setDebugInfo(list): list[i][1] must be string")
assert(type(list[i][2])=='function',"Z.setDebugInfo(list): list[i][2] must be function")
end
debugInfos=list
end
-- Change F1~F7 events of debugmode (F8 mode)
function Z.setOnFnKeys(list)
assert(type(list)=='table',"Z.setOnFnKeys(list): list must be table")
for i=1,7 do fnKey[i]=assert(type(list[i])=='function' and list[i]) end
end
function Z.setOnGlobalKey(key,func)
assert(type(key)=='string',"Z.setOnFnKeys(key,func): key must be string")
if not func then
globalKey[key]=nil
else
assert(type(func)=='function',"Z.setOnFnKeys(key,func): func must be function")
globalKey[key]=func
end
end
function Z.setOnFocus(func)
onFocus=assert(type(func)=='function' and func,"Z.setOnFocus(func): func must be function")
end
function Z.setOnResize(func)
onResize=assert(type(func)=='function' and func,"Z.setOnResize(func): func must be function")
end
function Z.setOnQuit(func)
onQuit=assert(type(func)=='function' and func,"Z.setOnQuit(func): func must be function")
end
function Z.setOnBeforeQuit(func)
onBeforeQuit=assert(type(func)=='function' and func,"Z.setOnBeforeQuit(func): func must be function")
end
return Z

340
Zframework/json.lua Normal file
View File

@@ -0,0 +1,340 @@
-- json.lua
-- Copyright (c) 2020 rxi
-- 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.
-- Editted by MrZ
local ins,char=table.insert,string.char
local json = {}
-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------
local _encode
local escape_char_map = {
["\\"] = "\\",
["\""] = "\"",
["\b"] = "b",
["\f"] = "f",
["\n"] = "n",
["\r"] = "r",
["\t"] = "t"
}
local escape_char_map_inv = {["/"] = "/"}
for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end
local function escape_char(c)
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
end
local function encode_nil() return "null" end
local function encode_table(val, stack)
local res = {}
stack = stack or {}
-- Circular reference?
if stack[val] then error("circular reference") end
stack[val] = true
if rawget(val, 1) ~= nil or next(val) == nil then
-- Treat as array -- check keys are valid and it is not sparse
local n = 0
for k in pairs(val) do
if type(k) ~= 'number' then
error("invalid table: mixed or invalid key types")
end
n = n + 1
end
if n ~= #val then error("invalid table: sparse array") end
-- Encode
for _, v in ipairs(val) do ins(res, _encode(v, stack)) end
stack[val] = nil
return "[" .. table.concat(res, ",") .. "]"
else
-- Treat as an object
for k, v in pairs(val) do
if type(k) ~= 'string' then
error("invalid table: mixed or invalid key types")
end
ins(res, _encode(k, stack) .. ":" .. _encode(v, stack))
end
stack[val] = nil
return "{" .. table.concat(res, ",") .. "}"
end
end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end
local function encode_number(val)
-- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'")
end
return string.format("%.14g", val)
end
local type_func_map = {
['nil'] = encode_nil,
['table'] = encode_table,
['string'] = encode_string,
['number'] = encode_number,
['boolean'] = tostring
}
_encode = function(val, stack)
local t = type(val)
local f = type_func_map[t]
if f then return f(val, stack) end
error("unexpected type '" .. t .. "'")
end
json.encode=_encode
-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------
local parse
local function create_set(...)
local res = {}
for i = 1, select("#", ...) do res[select(i, ...)] = true end
return res
end
local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null")
local literal_map = {["true"] = true, ["false"] = false, ["null"] = nil}
local function next_char(str, idx, set, negate)
for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end
return #str + 1
end
local function decode_error(str, idx, msg)
local line_count = 1
local col_count = 1
for i = 1, idx - 1 do
col_count = col_count + 1
if str:sub(i, i) == "\n" then
line_count = line_count + 1
col_count = 1
end
end
error(string.format("%s at line %d col %d", msg, line_count, col_count))
end
local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = bit.rshift
if n <= 0x7f then
return char(n)
elseif n <= 0x7ff then
return char(f(n, 6) + 192, n % 64 + 128)
elseif n <= 0xffff then
return char(f(n, 12) + 224, f(n % 4096, 6) + 128, n % 64 + 128)
elseif n <= 0x10ffff then
return char(f(n, 18) + 240, f(n % 262144, 12) + 128, f(n % 4096, 6) + 128, n % 64 + 128)
end
error(string.format("invalid unicode codepoint '%x'", n))
end
local function parse_unicode_escape(s)
local n1 = tonumber(s:sub(1, 4), 16)
local n2 = tonumber(s:sub(7, 10), 16)
-- Surrogate pair?
if n2 then
return
codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else
return codepoint_to_utf8(n1)
end
end
local function parse_string(str, i)
local res = ""
local j = i + 1
local k = j
while j <= #str do
local x = str:byte(j)
if x < 32 then
decode_error(str, j, "control character in string")
elseif x == 92 then -- `\`: Escape
res = res .. str:sub(k, j - 1)
j = j + 1
local c = str:sub(j, j)
if c == "u" then
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or
str:match("^%x%x%x%x", j + 1) or
decode_error(str, j - 1,
"invalid unicode escape in string")
res = res .. parse_unicode_escape(hex)
j = j + #hex
else
if not escape_chars[c] then
decode_error(str, j - 1,
"invalid escape char '" .. c .. "' in string")
end
res = res .. escape_char_map_inv[c]
end
k = j + 1
elseif x == 34 then -- `"`: End of string
res = res .. str:sub(k, j - 1)
return res, j + 1
end
j = j + 1
end
decode_error(str, i, "expected closing quote for string")
end
local function parse_number(str, i)
local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1)
local n = tonumber(s)
if not n then decode_error(str, i, "invalid number '" .. s .. "'") end
return n, x
end
local function parse_literal(str, i)
local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1)
if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'")
end
return literal_map[word], x
end
local function parse_array(str, i)
local res = {}
local n = 1
i = i + 1
while 1 do
local x
i = next_char(str, i, space_chars, true)
-- Empty / end of array?
if str:sub(i, i) == "]" then
i = i + 1
break
end
-- Read token
x, i = parse(str, i)
res[n] = x
n = n + 1
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end
return res, i
end
local function parse_object(str, i)
local res = {}
i = i + 1
while 1 do
local key, val
i = next_char(str, i, space_chars, true)
-- Empty / end of object?
if str:sub(i, i) == "}" then
i = i + 1
break
end
-- Read key
if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key")
end
key, i = parse(str, i)
-- Read ':' delimiter
i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key")
end
i = next_char(str, i + 1, space_chars, true)
-- Read value
val, i = parse(str, i)
-- Set
res[key] = val
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end
return res, i
end
local char_func_map = {
['"'] = parse_string,
["0"] = parse_number,
["1"] = parse_number,
["2"] = parse_number,
["3"] = parse_number,
["4"] = parse_number,
["5"] = parse_number,
["6"] = parse_number,
["7"] = parse_number,
["8"] = parse_number,
["9"] = parse_number,
["-"] = parse_number,
["t"] = parse_literal,
["f"] = parse_literal,
["n"] = parse_literal,
["["] = parse_array,
["{"] = parse_object
}
function parse(str, idx)
local chr = str:sub(idx, idx)
local f = char_func_map[chr]
if f then return f(str, idx) end
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
function json.decode(str)
if type(str) ~= 'string' then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then decode_error(str, idx, "trailing garbage") end
return res
end
return json

57
Zframework/languages.lua Normal file
View File

@@ -0,0 +1,57 @@
local LANG={}
-- ONLY FIRST CALL MAKE SENSE
-- Create LANG.get() and LANG.addScene()
function LANG.init(defaultLang,langList,publicText,pretreatFunc)
local function _langFallback(T0,T)
for k,v in next,T0 do
if type(v)=='table' and not v.refuseCopy then-- refuseCopy: just copy pointer, not contents
if not T[k] then T[k]={} end
if type(T[k])=='table' then
_langFallback(v,T[k])
end
elseif not T[k] then
T[k]=v
end
end
end
-- Set public text
if publicText then
for _,L in next,langList do
for key,list in next,publicText do L[key]=list end
end
end
-- Fallback to default language
for name,L in next,langList do
if name~=defaultLang then
_langFallback(langList[L.fallback or defaultLang],L)
end
end
-- Custom pretreatment for each language
if pretreatFunc then
for _,L in next,langList do
pretreatFunc(L)
end
end
function LANG.get(l)
if not langList[l] then
LOG("Wrong language: "..tostring(l))
l=defaultLang
end
return langList[l]
end
function LANG.addScene(name)
for _,L in next,langList do
if L.WidgetText and not L.WidgetText[name] then
L.WidgetText[name]={}
end
end
end
function LANG.init() end
end
return LANG

20
Zframework/log.lua Normal file
View File

@@ -0,0 +1,20 @@
local ins=table.insert
local logs={os.date("Techmino logs %Y/%m/%d %A")}
local function log(message)
ins(logs,os.date("[%H:%M:%S] ")..message)
end
local LOG=setmetatable({logs=logs},{
__call=function(_,message)
print(message)
log(message)
end
})
function LOG.read()
return table.concat(logs,"\n")
end
return LOG

26
Zframework/lowcaser.txt Normal file
View File

@@ -0,0 +1,26 @@
A=a,B=b,C=c,D=d,E=e,F=f,G=g,H=h,I=i,J=j,K=k,L=l,M=m,N=n,O=o,P=p,Q=q,R=r,S=s,T=t,U=u,V=v,W=w,X=x,Y=y,Z=z
Μ=µ,ẞ=ß,À=à,Á=á,Â=â,Ã=ã,Ä=ä,Å=å,Æ=æ,Ç=ç,È=è,É=é,Ê=ê,Ë=ë,Ì=ì,Í=í,Î=î,Ï=ï,Ð=ð,Ñ=ñ,Ò=ò,Ó=ó,Ô=ô,Õ=õ,Ö=ö,Ø=ø,Ù=ù,Ú=ú,Û=û,Ü=ü,Ý=ý,Þ=þ,Ÿ=ÿ,Ā=ā,Ă=ă,Ą=ą,Ć=ć,Ĉ=ĉ,Ċ=ċ,Č=č,Ď=ď,Đ=đ,Ē=ē,Ĕ=ĕ,Ė=ė,Ę=ę,Ě=ě,Ĝ=ĝ,Ğ=ğ,Ġ=ġ,Ģ=ģ,Ĥ=ĥ,Ħ=ħ,Ĩ=ĩ,Ī=ī,Ĭ=ĭ,Į=į,IJ=ij,Ĵ=ĵ,Ķ=ķ,Ĺ=ĺ,Ļ=ļ,Ľ=ľ,Ŀ=ŀ,Ł=ł,Ń=ń,Ņ=ņ,Ň=ň,Ŋ=ŋ,Ō=ō,Ŏ=ŏ,Ő=ő,Œ=œ,Ŕ=ŕ,Ŗ=ŗ,Ř=ř,Ś=ś,Ŝ=ŝ,Ş=ş,Š=š,Ţ=ţ,Ť=ť,Ŧ=ŧ,Ũ=ũ,Ū=ū,Ŭ=ŭ,Ů=ů,Ű=ű,Ų=ų,Ŵ=ŵ,Ŷ=ŷ,Ź=ź,Ż=ż,Ž=ž
Ƀ=ƀ,Ƃ=ƃ,Ƅ=ƅ,Ƈ=ƈ,Ƌ=ƌ,Ƒ=ƒ,Ƕ=ƕ,Ƙ=ƙ,Ƚ=ƚ,Ƞ=ƞ,Ơ=ơ,Ƣ=ƣ,Ƥ=ƥ,Ƨ=ƨ,Ƭ=ƭ,Ư=ư,Ƴ=ƴ,Ƶ=ƶ,Ƹ=ƹ,Ƽ=ƽ,Ƿ=ƿ,DŽ=dž,LJ=lj,NJ=nj,Ǎ=ǎ,Ǐ=ǐ,Ǒ=ǒ,Ǔ=ǔ,Ǖ=ǖ,Ǘ=ǘ,Ǚ=ǚ,Ǜ=ǜ,Ǝ=ǝ,Ǟ=ǟ,Ǡ=ǡ,Ǣ=ǣ,Ǥ=ǥ,Ǧ=ǧ,Ǩ=ǩ,Ǫ=ǫ,Ǭ=ǭ,Ǯ=ǯ,DZ=dz,Ǵ=ǵ,Ǹ=ǹ,Ǻ=ǻ,Ǽ=ǽ,Ǿ=ǿ,Ȁ=ȁ,Ȃ=ȃ,Ȅ=ȅ,Ȇ=ȇ,Ȉ=ȉ,Ȋ=ȋ,Ȍ=ȍ,Ȏ=ȏ,Ȑ=ȑ,Ȓ=ȓ,Ȕ=ȕ,Ȗ=ȗ,Ș=ș,Ț=ț,Ȝ=ȝ,Ȟ=ȟ,Ȣ=ȣ,Ȥ=ȥ,Ȧ=ȧ,Ȩ=ȩ,Ȫ=ȫ,Ȭ=ȭ,Ȯ=ȯ,Ȱ=ȱ,Ȳ=ȳ,Ȼ=ȼ,Ȿ=ȿ,Ɀ=ɀ,Ɂ=ɂ,Ɇ=ɇ,Ɉ=ɉ,Ɋ=ɋ,Ɍ=ɍ,Ɏ=ɏ
Ɐ=ɐ,Ɑ=ɑ,Ɒ=ɒ,Ɓ=ɓ,Ɔ=ɔ,Ɖ=ɖ,Ɗ=ɗ,Ə=ə,Ɛ=ɛ,=ɜ,Ɠ=ɠ,Ɡ=ɡ,Ɣ=ɣ,Ɥ=ɥ,Ɦ=ɦ,Ɨ=ɨ,Ɩ=ɩ,Ɪ=ɪ,Ɫ=ɫ,Ɬ=ɬ,Ɯ=ɯ,Ɱ=ɱ,Ɲ=ɲ,Ɵ=ɵ,Ɽ=ɽ,Ʀ=ʀ,Ʂ=ʂ,Ʃ=ʃ,Ʇ=ʇ,Ʈ=ʈ,Ʉ=ʉ,Ʊ=ʊ,Ʋ=ʋ,Ʌ=ʌ,Ʒ=ʒ,=ʝ,Ʞ=ʞ
Ͱ=ͱ,Ͳ=ͳ,Ͷ=ͷ,Ͻ=ͻ,Ͼ=ͼ,Ͽ=ͽ,Ά=ά,Έ=έ,Ή=ή,Ί=ί,Α=α,Β=β,Γ=γ,Δ=δ,Ε=ε,Ζ=ζ,Η=η,Θ=θ,Κ=κ,Λ=λ,Ν=ν,Ξ=ξ,Ο=ο,Π=π,Ρ=ρ,Σ=σ,Τ=τ,Υ=υ,Φ=φ,Χ=χ,Ψ=ψ,Ω=ω,Ϊ=ϊ,Ϋ=ϋ,Ό=ό,Ύ=ύ,Ώ=ώ,Ϗ=ϗ,Ϙ=ϙ,Ϛ=ϛ,Ϝ=ϝ,Ϟ=ϟ,Ϡ=ϡ,Ϣ=ϣ,Ϥ=ϥ,Ϧ=ϧ,Ϩ=ϩ,Ϫ=ϫ,Ϭ=ϭ,Ϯ=ϯ,Ϲ=ϲ,Ϳ=ϳ,Ϸ=ϸ,Ϻ
А=а,Б=б,В=в,Г=г,Д=д,Е=е,Ж=ж,З=з,И=и,Й=й,К=к,Л=л,М=м,Н=н,О=о,П=п,Р=р,С=с,Т=т,У=у,Ф=ф,Х=х,Ц=ц,Ч=ч,Ш=ш,Щ=щ,Ъ=ъ,Ы=ы,Ь=ь,Э=э,Ю=ю,Я=я,Ѐ=ѐ,Ё=ё,Ђ=ђ,Ѓ=ѓ,Є=є,Ѕ=ѕ,І=і,Ї=ї,Ј=ј,Љ=љ,Њ=њ,Ћ=ћ,Ќ=ќ,Ѝ=ѝ,Ў=ў,Џ=џ,Ѡ=ѡ,Ѣ=ѣ,Ѥ=ѥ,Ѧ=ѧ,Ѩ=ѩ,Ѫ=ѫ,Ѭ=ѭ,Ѯ=ѯ,Ѱ=ѱ,Ѳ=ѳ,Ѵ=ѵ,Ѷ=ѷ,Ѹ=ѹ,Ѻ=ѻ,Ѽ=ѽ,Ѿ=ѿ,Ҁ=ҁ,Ҋ=ҋ,Ҍ=ҍ,Ҏ=ҏ,Ґ=ґ,Ғ=ғ,Ҕ=ҕ,Җ=җ,Ҙ=ҙ,Қ=қ,Ҝ=ҝ,Ҟ=ҟ,Ҡ=ҡ,Ң=ң,Ҥ=ҥ,Ҧ=ҧ,Ҩ=ҩ,Ҫ=ҫ,Ҭ=ҭ,Ү=ү,Ұ=ұ,Ҳ=ҳ,Ҵ=ҵ,Ҷ=ҷ,Ҹ=ҹ,Һ=һ,Ҽ=ҽ,Ҿ=ҿ,Ӂ=ӂ,Ӄ=ӄ,Ӆ=ӆ,Ӈ=ӈ,Ӊ=ӊ,Ӌ=ӌ,Ӎ=ӎ,Ӏ=ӏ,Ӑ=ӑ,Ӓ=ӓ,Ӕ=ӕ,Ӗ=ӗ,Ә=ә,Ӛ=ӛ,Ӝ=ӝ,Ӟ=ӟ,Ӡ=ӡ,Ӣ=ӣ,Ӥ=ӥ,Ӧ=ӧ,Ө=ө,Ӫ=ӫ,Ӭ=ӭ,Ӯ=ӯ,Ӱ=ӱ,Ӳ=ӳ,Ӵ=ӵ,Ӷ=ӷ,Ӹ=ӹ,Ӻ=ӻ,Ӽ=ӽ,Ӿ=ӿ,Ԁ=ԁ,Ԃ=ԃ,Ԅ=ԅ,Ԇ=ԇ,Ԉ=ԉ,Ԋ=ԋ,Ԍ=ԍ,Ԏ=ԏ,Ԑ=ԑ,Ԓ=ԓ,Ԕ=ԕ,Ԗ=ԗ,Ԙ=ԙ,Ԛ=ԛ,Ԝ=ԝ,Ԟ=ԟ,Ԡ=ԡ,Ԣ=ԣ,Ԥ=ԥ,Ԧ=ԧ,Ԩ=ԩ,Ԫ=ԫ,Ԭ=ԭ,Ԯ=ԯ
Ա=ա,Բ=բ,Գ=գ,Դ=դ,Ե=ե,Զ=զ,Է=է,Ը=ը,Թ=թ,Ժ=ժ,Ի=ի,Լ=լ,Խ=խ,Ծ=ծ,Կ=կ,Հ=հ,Ձ=ձ,Ղ=ղ,Ճ=ճ,Մ=մ,Յ=յ,Ն=ն,Շ=շ,Ո=ո,Չ=չ,Պ=պ,Ջ=ջ,Ռ=ռ,Ս=ս,Վ=վ,Տ=տ,Ր=ր,Ց=ց,Ւ=ւ,Փ=փ,Ք=ք,Օ=օ,Ֆ=ֆ
Ა=ა,Ბ=ბ,Გ=გ,Დ=დ,Ე=ე,Ვ=ვ,Ზ=ზ,Თ=თ,Ი=ი,Კ=კ,Ლ=ლ,Მ=მ,Ნ=ნ,Ო=ო,Პ=პ,Ჟ=ჟ,Რ=რ,Ს=ს,Ტ=ტ,Უ=უ,Ფ=ფ,Ქ=ქ,Ღ=ღ,Ყ=,Შ=შ,Ჩ=ჩ,Ც=ც,Ძ=ძ,Წ=წ,Ჭ=ჭ,Ხ=ხ,Ჯ=ჯ,Ჰ=ჰ,Ჱ=ჱ,Ჲ=ჲ,Ჳ=ჳ,Ჴ=ჴ,Ჵ=ჵ,Ჶ=ჶ,Ჷ=ჷ,Ჸ=ჸ,Ჹ=ჹ,Ჺ=ჺ,Ჽ=ჽ,Ჾ=ჾ,Ჿ=
Ᏸ=ᏸ,Ᏹ=ᏹ,Ᏺ=ᏺ,=ᏻ,=ᏼ,Ᏽ=ᏽ
Ꙋ=ᲈ,Ᵹ=ᵹ,Ᵽ=ᵽ,Ᶎ=ᶎ,Ḁ=ḁ,Ḃ=ḃ,Ḅ=ḅ,Ḇ=ḇ,Ḉ=ḉ,Ḋ=ḋ,Ḍ=ḍ,Ḏ=ḏ,Ḑ=ḑ,Ḓ=ḓ,Ḕ=ḕ,Ḗ=ḗ,Ḙ=ḙ,Ḛ=ḛ,Ḝ=ḝ,Ḟ=ḟ,Ḡ=ḡ,Ḣ=ḣ,Ḥ=ḥ,Ḧ=ḧ,Ḩ=ḩ,Ḫ=ḫ,Ḭ=ḭ,Ḯ=ḯ,Ḱ=ḱ,Ḳ=ḳ,Ḵ=ḵ,Ḷ=ḷ,Ḹ=ḹ,Ḻ=ḻ,Ḽ=ḽ,Ḿ=ḿ,Ṁ=ṁ,Ṃ=ṃ,Ṅ=ṅ,Ṇ=ṇ,Ṉ=ṉ,Ṋ=ṋ,Ṍ=ṍ,Ṏ=ṏ,Ṑ=ṑ,Ṓ=ṓ,Ṕ=ṕ,Ṗ=ṗ,Ṙ=ṙ,Ṛ=ṛ,Ṝ=ṝ,Ṟ=ṟ,Ṡ=ṡ,Ṣ=ṣ,Ṥ=ṥ,Ṧ=ṧ,Ṩ=ṩ,Ṫ=ṫ,Ṭ=ṭ,Ṯ=ṯ,Ṱ=ṱ,Ṳ=ṳ,Ṵ=ṵ,Ṷ=ṷ,Ṹ=ṹ,Ṻ=ṻ,Ṽ=ṽ,Ṿ=ṿ,Ẁ=ẁ,Ẃ=ẃ,Ẅ=ẅ,Ẇ=ẇ,Ẉ=ẉ,Ẋ=ẋ,Ẍ=ẍ,Ẏ=ẏ,Ẑ=ẑ,Ẓ=ẓ,Ẕ=ẕ,Ạ=ạ,Ả=ả,Ấ=ấ,Ầ=ầ,Ẩ=ẩ,Ẫ=ẫ,Ậ=ậ,Ắ=ắ,Ằ=ằ,Ẳ=ẳ,Ẵ=ẵ,Ặ=ặ,Ẹ=ẹ,Ẻ=ẻ,Ẽ=ẽ,Ế=ế,Ề=ề,Ể=ể,Ễ=ễ,Ệ=ệ,Ỉ=ỉ,Ị=ị,Ọ=ọ,Ỏ=ỏ,Ố=ố,Ồ=ồ,Ổ=ổ,Ỗ=ỗ,Ộ=ộ,Ớ=ớ,Ờ=ờ,Ở=ở,Ỡ=ỡ,Ợ=ợ,Ụ=ụ,Ủ=ủ,Ứ=ứ,Ừ=ừ,Ử=ử,Ữ=ữ,Ự=ự,Ỳ=ỳ,Ỵ=ỵ,Ỷ=ỷ,Ỹ=ỹ,Ỻ=ỻ,Ỽ=ỽ,Ỿ=ỿ,Ἀ=ἀ,Ἁ=ἁ,Ἂ=ἂ,Ἃ=ἃ,Ἄ=ἄ,Ἅ=ἅ,Ἆ=ἆ,Ἇ=ἇ,Ἐ=ἐ,Ἑ=ἑ,Ἒ=ἒ,Ἓ=ἓ,Ἔ=ἔ,Ἕ=ἕ,Ἠ=ἠ,Ἡ=ἡ,Ἢ=ἢ,Ἣ=ἣ,Ἤ=ἤ,Ἥ=ἥ,Ἦ=ἦ,Ἧ=ἧ,Ἰ=ἰ,Ἱ=ἱ,Ἲ=ἲ,Ἳ=ἳ,Ἴ=ἴ,Ἵ=ἵ,Ἶ=ἶ,Ἷ=ἷ,Ὀ=ὀ,Ὁ=ὁ,Ὂ=ὂ,Ὃ=ὃ,Ὄ=ὄ,Ὅ=ὅ,Ὑ=ὑ,Ὓ=ὓ,Ὕ=ὕ,Ὗ=ὗ,Ὠ=ὠ,Ὡ=ὡ,Ὢ=ὢ,Ὣ=ὣ,Ὤ=ὤ,Ὥ=ὥ,Ὦ=ὦ,Ὧ=ὧ,Ὰ=ὰ,Ά=ά,Ὲ=ὲ,Έ=έ,Ὴ=ὴ,Ή=ή,Ὶ=ὶ,Ί=ί,Ὸ=ὸ,Ό=ό,Ὺ=ὺ,Ύ=ύ,Ὼ=ὼ,Ώ=ώ,Ᾰ=ᾰ,Ᾱ=ᾱ,Ῐ=ῐ,Ῑ=ῑ,Ῠ=ῠ,Ῡ=ῡ,Ῥ=ῥ,Ⅎ=ⅎ
=,Ⅱ=ⅱ,Ⅲ=ⅲ,Ⅳ=ⅳ,=,Ⅵ=ⅵ,Ⅶ=ⅶ,Ⅷ=ⅷ,Ⅸ=ⅸ,=,Ⅺ=ⅺ,Ⅻ=ⅻ,=,=,=,=ⅿ,Ↄ=ↄ
Ⓐ=ⓐ,Ⓑ=ⓑ,Ⓒ=ⓒ,Ⓓ=ⓓ,Ⓔ=ⓔ,Ⓕ=ⓕ,Ⓖ=ⓖ,Ⓗ=ⓗ,Ⓘ=ⓘ,Ⓙ=ⓙ,Ⓚ=ⓚ,Ⓛ=ⓛ,Ⓜ=ⓜ,Ⓝ=ⓝ,Ⓞ=ⓞ,Ⓟ=ⓟ,Ⓠ=ⓠ,Ⓡ=ⓡ,Ⓢ=ⓢ,Ⓣ=ⓣ,Ⓤ=ⓤ,Ⓥ=ⓥ,Ⓦ=ⓦ,Ⓧ=ⓧ,Ⓨ=ⓨ,Ⓩ=ⓩ
Ⰰ=ⰰ,Ⰱ=ⰱ,Ⰲ=ⰲ,Ⰳ=ⰳ,Ⰴ=ⰴ,Ⰵ=ⰵ,Ⰶ=ⰶ,Ⰷ=ⰷ,Ⰸ=ⰸ,Ⰹ=ⰹ,Ⰺ=ⰺ,Ⰻ=ⰻ,Ⰼ=ⰼ,Ⰽ=ⰽ,Ⰾ=ⰾ,Ⰿ=ⰿ,Ⱀ=ⱀ,Ⱁ=ⱁ,Ⱂ=ⱂ,Ⱃ=ⱃ,Ⱄ=ⱄ,Ⱅ=ⱅ,Ⱆ=ⱆ,Ⱇ=ⱇ,Ⱈ=ⱈ,Ⱉ=ⱉ,Ⱊ=ⱊ,Ⱋ=ⱋ,Ⱌ=ⱌ,Ⱍ=ⱍ,Ⱎ=ⱎ,Ⱏ=ⱏ,Ⱐ=ⱐ,Ⱑ=ⱑ,Ⱒ=ⱒ,Ⱓ=ⱓ,Ⱔ=ⱔ,Ⱕ=ⱕ,Ⱖ=ⱖ,Ⱗ=ⱗ,Ⱘ=ⱘ,Ⱙ=ⱙ,Ⱚ=ⱚ,Ⱛ=ⱛ,Ⱜ=ⱜ,Ⱝ=ⱝ,Ⱞ=ⱞ,Ⱟ=ⱟ
Ⱡ=ⱡ,Ⱥ=ⱥ,Ⱦ=ⱦ,Ⱨ=ⱨ,Ⱪ=ⱪ,Ⱬ=ⱬ,Ⱳ=ⱳ,Ⱶ=ⱶ
Ⲁ=ⲁ,Ⲃ=ⲃ,Ⲅ=,Ⲇ=ⲇ,Ⲉ=ⲉ,Ⲋ=ⲋ,Ⲍ=ⲍ,=ⲏ,Ⲑ=ⲑ,=ⲓ,=ⲕ,Ⲗ=ⲗ,=ⲙ,=ⲛ,Ⲝ=ⲝ,=,Ⲡ=ⲡ,=,=,=ⲧ,=ⲩ,Ⲫ=ⲫ,=ⲭ,Ⲯ=ⲯ,Ⲱ=ⲱ,Ⲳ=ⲳ,Ⲵ=ⲵ,Ⲷ=ⲷ,Ⲹ=ⲹ,=ⲻ,Ⲽ=ⲽ,Ⲿ=ⲿ,Ⳁ=ⳁ,Ⳃ=ⳃ,Ⳅ=ⳅ,=ⳇ,Ⳉ=ⳉ,=ⳋ,=ⳍ,Ⳏ=ⳏ,=ⳑ,=ⳓ,Ⳕ=ⳕ,Ⳗ=ⳗ,Ⳙ=ⳙ,Ⳛ=ⳛ,Ⳝ=ⳝ,Ⳟ=ⳟ,Ⳡ=ⳡ,Ⳣ=ⳣ,Ⳬ=ⳬ,Ⳮ=ⳮ,Ⳳ=ⳳ
Ⴀ=ⴀ,Ⴁ=ⴁ,Ⴂ=ⴂ,Ⴃ=ⴃ,Ⴄ=ⴄ,Ⴅ=ⴅ,Ⴆ=ⴆ,Ⴇ=ⴇ,Ⴈ=ⴈ,Ⴉ=ⴉ,Ⴊ=ⴊ,Ⴋ=ⴋ,Ⴌ=ⴌ,Ⴍ=ⴍ,Ⴎ=ⴎ,Ⴏ=ⴏ,Ⴐ=ⴐ,Ⴑ=ⴑ,Ⴒ=ⴒ,Ⴓ=ⴓ,Ⴔ=ⴔ,Ⴕ=ⴕ,Ⴖ=ⴖ,Ⴗ=ⴗ,Ⴘ=ⴘ,Ⴙ=ⴙ,Ⴚ=ⴚ,Ⴛ=ⴛ,Ⴜ=ⴜ,Ⴝ=ⴝ,Ⴞ=ⴞ,Ⴟ=ⴟ,Ⴠ=ⴠ,Ⴡ=ⴡ,Ⴢ=ⴢ,Ⴣ=ⴣ,Ⴤ=ⴤ,Ⴥ=ⴥ,Ⴧ=ⴧ
Ⴭ=ⴭ,Ꙁ=ꙁ,Ꙃ=ꙃ,=ꙅ,Ꙇ=,Ꙉ=ꙉ,Ꙍ=ꙍ,Ꙏ=ꙏ,Ꙑ=ꙑ,Ꙓ=ꙓ,Ꙕ=ꙕ,Ꙗ=ꙗ,Ꙙ=ꙙ,Ꙛ=ꙛ,Ꙝ=ꙝ,Ꙟ=ꙟ,Ꙡ=ꙡ,Ꙣ=ꙣ,Ꙥ=ꙥ,Ꙧ=ꙧ,Ꙩ=ꙩ,Ꙫ=ꙫ,Ꙭ=ꙭ,Ꚁ=ꚁ,Ꚃ=ꚃ,Ꚅ=ꚅ,Ꚇ=ꚇ,Ꚉ=ꚉ,Ꚋ=ꚋ,Ꚍ=ꚍ,Ꚏ=ꚏ,Ꚑ=ꚑ,Ꚓ=ꚓ,Ꚕ=ꚕ,Ꚗ=ꚗ,Ꚙ=ꚙ,Ꚛ=ꚛ,Ꜣ=ꜣ,Ꜥ=ꜥ,Ꜧ=ꜧ,Ꜩ=ꜩ,Ꜫ=ꜫ,Ꜭ=ꜭ,Ꜯ=ꜯ,Ꜳ=ꜳ,Ꜵ=ꜵ,Ꜷ=ꜷ,Ꜹ=ꜹ,Ꜻ=ꜻ,Ꜽ=ꜽ,Ꜿ=ꜿ,Ꝁ=ꝁ,Ꝃ=ꝃ,Ꝅ=ꝅ,Ꝇ=ꝇ,Ꝉ=ꝉ,Ꝋ=ꝋ,Ꝍ=ꝍ,Ꝏ=ꝏ,Ꝑ=ꝑ,Ꝓ=ꝓ,Ꝕ=ꝕ,Ꝗ=ꝗ,Ꝙ=ꝙ,=ꝛ,Ꝝ=ꝝ,Ꝟ=ꝟ,Ꝡ=ꝡ,Ꝣ=ꝣ,Ꝥ=ꝥ,Ꝧ=ꝧ,Ꝩ=ꝩ,=ꝫ,Ꝭ=ꝭ,=ꝯ,Ꝺ=ꝺ,Ꝼ=ꝼ,Ꝿ=ꝿ,Ꞁ=ꞁ,Ꞃ=ꞃ,Ꞅ=ꞅ,Ꞇ=ꞇ,Ꞌ=,Ꞑ=ꞑ,Ꞓ=ꞓ,Ꞔ=ꞔ,Ꞗ=ꞗ,=,Ꞛ=ꞛ,Ꞝ=ꞝ,Ꞟ=,Ꞡ=ꞡ,Ꞣ=ꞣ,Ꞥ=ꞥ,Ꞧ=ꞧ,Ꞩ=ꞩ,=ꞵ,Ꞷ=ꞷ,Ꞹ=ꞹ,Ꞻ=ꞻ,Ꞽ=ꞽ,Ꞿ=ꞿ,Ꟁ=ꟁ,Ꟃ=ꟃ,Ꟈ=ꟈ,Ꟊ=ꟊ,Ꟑ=ꟑ,Ꟗ=ꟗ,Ꟙ=ꟙ,Ꟶ=ꟶ,=ꭓ
=ꭰ,=ꭱ,=ꭲ,Ꭳ=ꭳ,Ꭴ=ꭴ,=,Ꭶ=ꭶ,Ꭷ=ꭷ,Ꭸ=ꭸ,=ꭹ,=ꭺ,=ꭻ,=ꭼ,Ꭽ=ꭽ,=ꭾ,Ꭿ=ꭿ,Ꮀ=ꮀ,Ꮁ=,Ꮂ=ꮂ,=,Ꮄ=ꮄ,Ꮅ=ꮅ,Ꮆ=ꮆ,=ꮇ,Ꮈ=ꮈ,Ꮉ=ꮉ,Ꮊ=ꮊ,=ꮋ,Ꮌ=ꮌ,=ꮍ,Ꮎ=ꮎ,Ꮏ=ꮏ,=ꮐ,Ꮑ=ꮑ,=ꮒ,=,Ꮔ=ꮔ,Ꮕ=ꮕ,Ꮖ=ꮖ,Ꮗ=ꮗ,Ꮘ=ꮘ,Ꮙ=ꮙ,Ꮚ=ꮚ,Ꮛ=ꮛ,Ꮜ=ꮜ,Ꮝ=ꮝ,=ꮞ,=ꮟ,Ꮠ=ꮠ,Ꮡ=ꮡ,=ꮢ,Ꮣ=ꮣ,=ꮤ,=ꮥ,Ꮦ=ꮦ,Ꮧ=ꮧ,Ꮨ=ꮨ,=,=,Ꮫ=ꮫ,Ꮬ=ꮬ,Ꮭ=ꮭ,=ꮮ,=,Ꮰ=ꮰ,Ꮱ=ꮱ,=ꮲ,Ꮳ=ꮳ,Ꮴ=ꮴ,Ꮵ=ꮵ,=ꮶ,=ꮷ,Ꮸ=ꮸ,Ꮹ=ꮹ,Ꮺ=ꮺ,Ꮻ=ꮻ,Ꮼ=ꮼ,Ꮽ=ꮽ,=ꮾ,Ꮿ=ꮿ
=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=
𐐀=𐐨,𐐁=𐐩,𐐂=𐐪,𐐃=𐐫,𐐄=𐐬,𐐅=𐐭,𐐆=𐐮,𐐇=𐐯,𐐈=𐐰,𐐉=𐐱,𐐊=𐐲,𐐋=𐐳,𐐌=𐐴,𐐍=𐐵,𐐎=𐐶,𐐏=𐐷,𐐐=𐐸,𐐑=𐐹,𐐒=𐐺,𐐓=𐐻,𐐔=𐐼,𐐕=𐐽,𐐖=𐐾,𐐗=𐐿,𐐘=𐑀,𐐙=𐑁,𐐚=𐑂,𐐛=𐑃,𐐜=𐑄,𐐝=𐑅,𐐞=𐑆,𐐟=𐑇,𐐠=𐑈,𐐡=𐑉,𐐢=𐑊,𐐣=𐑋,𐐤=𐑌,𐐥=𐑍,𐐦=𐑎,𐐧=𐑏
𐒰=𐓘,𐒱=𐓙,𐒲=𐓚,𐒳=𐓛,𐒴=𐓜,𐒵=𐓝,𐒶=𐓞,𐒷=𐓟,𐒸=𐓠,𐒹=𐓡,𐒺=𐓢,𐒻=𐓣,𐒼=𐓤,𐒽=𐓥,𐒾=𐓦,𐒿=𐓧,𐓀=𐓨,𐓁=𐓩,𐓂=𐓪,𐓃=𐓫,𐓄=𐓬,𐓅=𐓭,𐓆=𐓮,𐓇=𐓯,𐓈=𐓰,𐓉=𐓱,𐓊=𐓲,𐓋=𐓳,𐓌=𐓴,𐓍=𐓵,𐓎=𐓶,𐓏=𐓷,𐓐=𐓸,𐓑=𐓹,𐓒=𐓺,𐓓=𐓻
𐲀=𐳀,𐲁=𐳁,𐲂=𐳂,𐲃=𐳃,𐲄=𐳄,𐲅=𐳅,𐲆=𐳆,𐲇=𐳇,𐲈=𐳈,𐲉=𐳉,𐲊=𐳊,𐲋=𐳋,𐲌=𐳌,𐲍=𐳍,𐲎=𐳎,𐲏=𐳏,𐲐=𐳐,𐲑=𐳑,𐲒=𐳒,𐲓=𐳓,𐲔=𐳔,𐲕=𐳕,𐲖=𐳖,𐲗=𐳗,𐲘=𐳘,𐲙=𐳙,𐲚=𐳚,𐲛=𐳛,𐲜=𐳜,𐲝=𐳝,𐲞=𐳞,𐲟=𐳟,𐲠=𐳠,𐲡=𐳡,𐲢=𐳢,𐲣=𐳣,𐲤=𐳤,𐲥=𐳥,𐲦=𐳦,𐲧=𐳧,𐲨=𐳨,𐲩=𐳩,𐲪=𐳪,𐲫=𐳫,𐲬=𐳬,𐲭=𐳭,𐲮=𐳮,𐲯=𐳯,𐲰=𐳰,𐲱=𐳱,𐲲=𐳲
𑢠=𑣀,𑢡=𑣁,𑢢=𑣂,𑢣=𑣃,𑢤=𑣄,𑢥=𑣅,𑢦=𑣆,𑢧=𑣇,𑢨=𑣈,𑢩=𑣉,𑢪=𑣊,𑢫=𑣋,𑢬=𑣌,𑢭=𑣍,𑢮=𑣎,𑢯=𑣏,𑢰=𑣐,𑢱=𑣑,𑢲=𑣒,𑢳=𑣓,𑢴=𑣔,𑢵=𑣕,𑢶=𑣖,𑢷=𑣗,𑢸=𑣘,𑢹=𑣙,𑢺=𑣚,𑢻=𑣛,𑢼=𑣜,𑢽=𑣝,𑢾=𑣞,𑢿=𑣟
𖹀=𖹠,𖹁=𖹡,𖹂=𖹢,𖹃=𖹣,𖹄=𖹤,𖹅=𖹥,𖹆=𖹦,𖹇=𖹧,𖹈=𖹨,𖹉=𖹩,𖹊=𖹪,𖹋=𖹫,𖹌=𖹬,𖹍=𖹭,𖹎=𖹮,𖹏=𖹯,𖹐=𖹰,𖹑=𖹱,𖹒=𖹲,𖹓=𖹳,𖹔=𖹴,𖹕=𖹵,𖹖=𖹶,𖹗=𖹷,𖹘=𖹸,𖹙=𖹹,𖹚=𖹺,𖹛=𖹻,𖹜=𖹼,𖹝=𖹽,𖹞=𖹾,𖹟=𖹿
𞤀=𞤢,𞤁=𞤣,𞤂=𞤤,𞤃=𞤥,𞤄=𞤦,𞤅=𞤧,𞤆=𞤨,𞤇=𞤩,𞤈=𞤪,𞤉=𞤫,𞤊=𞤬,𞤋=𞤭,𞤌=𞤮,𞤍=𞤯,𞤎=𞤰,𞤏=𞤱,𞤐=𞤲,𞤑=𞤳,𞤒=𞤴,𞤓=𞤵,𞤔=𞤶,𞤕=𞤷,𞤖=𞤸,𞤗=𞤹,𞤘=𞤺,𞤙=𞤻,𞤚=𞤼,𞤛=𞤽,𞤜=𞤾,𞤝=𞤿,𞤞=𞥀,𞤟=𞥁,𞤠=𞥂,𞤡=𞥃
İ=i̇,ʼN=ʼn,J̌=ǰ,Ϊ́=ΐ,Ϋ́=ΰ,ԵՒ=և,H̱=ẖ,T̈=ẗ,W̊=ẘ,Y̊=ẙ,Aʾ=ẚ,Υ̓=ὐ,Υ̓̀=ὒ,Υ̓́=ὔ,Υ̓͂=ὖ,ἈΙ=ᾀ,ἉΙ=ᾁ,ἊΙ=ᾂ,ἋΙ=ᾃ,ἌΙ=ᾄ,ἍΙ=ᾅ,ἎΙ=ᾆ,ἏΙ=ᾇ,ἨΙ=ᾐ,ἩΙ=ᾑ,ἪΙ=ᾒ,ἫΙ=ᾓ,ἬΙ=ᾔ,ἭΙ=ᾕ,ἮΙ=ᾖ,ἯΙ=ᾗ,ὨΙ=ᾠ,ὩΙ=ᾡ,ὪΙ=ᾢ,ὫΙ=ᾣ,ὬΙ=ᾤ,ὭΙ=ᾥ,ὮΙ=ᾦ,ὯΙ=ᾧ,ᾺΙ=ᾲ,ΑΙ=ᾳ,ΆΙ=ᾴ,Α͂=ᾶ,Α͂Ι=ᾷ,ῊΙ=ῂ,ΗΙ=ῃ,ΉΙ=ῄ,Η͂=ῆ,Η͂Ι=ῇ,Ϊ̀=ῒ,Ι͂=ῖ,Ϊ͂=ῗ,Ϋ̀=ῢ,Ρ̓=ῤ,Υ͂=ῦ,Ϋ͂=ῧ,ῺΙ=ῲ,ΩΙ=ῳ,ΏΙ=ῴ,Ω͂=ῶ,Ω͂Ι=ῷ,ՄՆ=ﬓ,ՄԵ=ﬔ,ՄԻ=ﬕ,ՎՆ=ﬖ,ՄԽ=ﬗ

88
Zframework/mathExtend.lua Normal file
View File

@@ -0,0 +1,88 @@
local MATH={} for k,v in next,math do MATH[k]=v end
local floor,ceil=math.floor,math.ceil
local rnd=math.random
local exp=math.exp
MATH.tau=2*math.pi
MATH.phi=(1+math.sqrt(5))/2
MATH.inf=1/0
MATH.nan=0/0
function MATH.isnan(n)
return n~=n
end
function MATH.sign(a)
return a>0 and 1 or a<0 and -1 or 0
end
function MATH.roll(chance)
return rnd()<(chance or .5)
end
function MATH.coin(a,b)
if rnd()<.5 then
return a
else
return b
end
end
function MATH.clamp(v,low,high)
if v<=low then
return low
elseif v>=high then
return high
else
return v
end
end
function MATH.mix(s,e,t)
return s+(e-s)*t
end
do-- function MATH.listMix(list,t)
local clamp,mix=MATH.clamp,MATH.mix
function MATH.listMix(list,t)
local t2=(#list-1)*clamp(t,0,1)+1
return mix(list[floor(t2)],list[ceil(t2)],t2%1)
end
end
function MATH.expApproach(a,b,k)
return b+(a-b)*exp(-k)
end
function MATH.distance(x1,y1,x2,y2)
return ((x1-x2)^2+(y1-y2)^2)^.5
end
-- By Pedro Gimeno,donated to the public domain
function MATH.pointInPolygon(x,y,poly,evenOddRule)
local x1,y1,x2,y2
local len=#poly
x2,y2=poly[len-1],poly[len]
local wn=0
for idx=1,len,2 do
x1,y1=x2,y2
x2,y2=poly[idx],poly[idx+1]
if y1>y then
if y2<=y and (x1-x)*(y2-y)<(x2-x)*(y1-y) then
wn=wn+1
end
else
if y2>y and (x1-x)*(y2-y)>(x2-x)*(y1-y) then
wn=wn-1
end
end
end
if evenOddRule then
return wn%2~=0
else-- non-zero winding rule
return wn~=0
end
end
return MATH

153
Zframework/message.lua Normal file
View File

@@ -0,0 +1,153 @@
local ins,rem=table.insert,table.remove
local max=math.max
local mesList={}
local mesIcon={
check=GC.DO{40,40,
{'setLW',10},
{'setCL',0,0,0},
{'line',4,19,15,30,36,9},
{'setLW',6},
{'setCL',.7,1,.6},
{'line',5,20,15,30,35,10},
},
info=GC.DO{40,40,
{'setCL',.2,.25,.85},
{'fCirc',20,20,15},
{'setCL',1,1,1},
{'setLW',2},
{'dCirc',20,20,15},
{'fRect',18,11,4,4},
{'fRect',18,17,4,12},
},
broadcast=GC.DO{40,40,
{'setCL',1,1,1},
{'fRect',2,4,36,26,3},
{'fPoly',2,27,2,37,14,25},
{'setCL',.5,.5,.5},
{'fRect',6,11,4,4,1},{'fRect',14,11,19,4,1},
{'fRect',6,19,4,4,1},{'fRect',14,19,19,4,1},
},
warn=GC.DO{40,40,
{'setCL',.95,.83,.4},
{'fPoly',20.5,1,0,38,40,38},
{'setCL',0,0,0},
{'dPoly',20.5,1,0,38,40,38},
{'fRect',17,10,7,18,2},
{'fRect',17,29,7,7,2},
{'setCL',1,1,1},
{'fRect',18,11,5,16,2},
{'fRect',18,30,5,5,2},
},
error=GC.DO{40,40,
{'setCL',.95,.3,.3},
{'fCirc',20,20,19},
{'setCL',0,0,0},
{'dCirc',20,20,19},
{'setLW',6},
{'line',10.2,10.2,29.8,29.8},
{'line',10.2,29.8,29.8,10.2},
{'setLW',4},
{'setCL',1,1,1},
{'line',11,11,29,29},
{'line',11,29,29,11},
},
music=GC.DO{40,40,
{'setLW',2},
{'dRect',1,3,38,34,3},
{'setLW',4},
{'line',21,26,21,10,28,10},
{'fElps',17,26,6,5},
},
}
local MES={}
local backColors={
check={.3,.6,.3,.7},
broadcast={.3,.3,.6,.8},
warn={.4,.4,.2,.9},
error={.4,.2,.2,.9},
music={.2,.4,.4,.9},
other={.5,.5,.5,.7},
}
function MES.new(icon,str,time)
local color=backColors.other
if type(icon)=='string' then
color=TABLE.shift(backColors[icon] or color)
icon=mesIcon[icon]
end
local text=GC.newText(FONT.get(30),str)
local w=math.max(text:getWidth()+(icon and 45 or 5),200)+15
local h=math.max(text:getHeight(),46)+2
local k=h>400 and 1/math.min(h/400,2.6) or 1
ins(mesList,1,{
startTime=.26,
endTime=.26,
time=time or 3,
color=color,
text=text,icon=icon,
w=w,h=h,k=k,
y=-h,
})
end
function MES.update(dt)
for i=#mesList,1,-1 do
local m=mesList[i]
if m.startTime>0 then
m.startTime=max(m.startTime-dt,0)
elseif m.time>0 then
m.time=max(m.time-dt,0)
elseif m.endTime>0 then
m.endTime=m.endTime-dt
else
rem(mesList,i)
end
if i>1 then
local _m=mesList[i-1]
m.y=MATH.expApproach(m.y,_m.y+_m.h*_m.k+3,dt*26)
else
m.y=MATH.expApproach(m.y,3,dt*26)
end
end
end
function MES.draw()
if #mesList>0 then
GC.setLineWidth(2)
for i=1,#mesList do
local m=mesList[i]
local a=3.846*(m.endTime-m.startTime)
GC.push('transform')
GC.translate(3+SCR.safeX,m.y)
GC.scale(m.k)
GC.setColor(m.color[1],m.color[2],m.color[3],m.color[4]*a)
GC.rectangle('fill',0,0,m.w,m.h,8)
GC.setColor(.62,.62,.62,a*.626)
GC.rectangle('line',1,1,m.w-2,m.h-2,4)
GC.setColor(1,1,1,a)
if m.icon then
GC.draw(m.icon,4,4,nil,40/m.icon:getWidth(),40/m.icon:getHeight())
end
GC.simpY(m.text,m.icon and 50 or 10,m.h/2)
GC.pop()
end
end
end
function MES.traceback()
local mes=
debug.traceback('',1)
:gsub(': in function',', in')
:gsub(':',' ')
:gsub('\t','')
MES.new('error',mes:sub(
mes:find("\n",2)+1,
mes:find("\n%[C%], in 'xpcall'")
),5)
end
return MES

157
Zframework/profile.lua Normal file
View File

@@ -0,0 +1,157 @@
local clock=os.clock
local profile={}
local _labeled={} -- function labels
local _defined={} -- function definitions
local _tcalled={} -- time of last call
local _telapsed={}-- total execution time
local _ncalls={} -- number of calls
local _internal={}-- list of internal profiler functions
local getInfo=debug.getinfo
function profile.hooker(event,line,info)
info=info or getInfo(2,'fnS')
local f=info.func
if _internal[f] then return end-- ignore the profiler itself
if info.name then _labeled[f]=info.name end-- get the function name if available
-- find the line definition
if not _defined[f] then
_defined[f]=info.short_src..":"..info.linedefined
_ncalls[f]=0
_telapsed[f]=0
end
if _tcalled[f] then
local dt=clock()-_tcalled[f]
_telapsed[f]=_telapsed[f]+dt
_tcalled[f]=nil
end
if event=='tail call' then
local prev=getInfo(3,'fnS')
profile.hooker('return',line,prev)
profile.hooker('call',line,info)
elseif event=='call' then
_tcalled[f]=clock()
else
_ncalls[f]=_ncalls[f]+1
end
end
--- Starts collecting data.
function profile.start()
if jit then
jit.off()
jit.flush()
end
debug.sethook(profile.hooker,'cr')
end
--- Stops collecting data.
function profile.stop()
debug.sethook()
for f in next,_tcalled do
local dt=clock()-_tcalled[f]
_telapsed[f]=_telapsed[f]+dt
_tcalled[f]=nil
end
-- merge closures
local lookup={}
for f,d in next,_defined do
local id=(_labeled[f] or "?")..d
local f2=lookup[id]
if f2 then
_ncalls[f2]=_ncalls[f2]+(_ncalls[f] or 0)
_telapsed[f2]=_telapsed[f2]+(_telapsed[f] or 0)
_defined[f],_labeled[f]=nil,nil
_ncalls[f],_telapsed[f]=nil,nil
else
lookup[id]=f
end
end
collectgarbage()
end
--- Resets all collected data.
function profile.reset()
for f in next,_ncalls do
_ncalls[f]=0
_telapsed[f]=0
_tcalled[f]=nil
end
collectgarbage()
end
local function _comp(a,b)
local dt=_telapsed[b]-_telapsed[a]
return dt==0 and _ncalls[b]<_ncalls[a] or dt<0
end
--- Iterates all functions that have been called since the profile was started.
function profile.query(limit)
local t={}
for f,n in next,_ncalls do
if n>0 then
t[#t+1]=f
end
end
table.sort(t,_comp)
if limit then while #t>limit do table.remove(t) end end
for i,f in ipairs(t) do
local dt=0
if _tcalled[f] then
dt=clock()-_tcalled[f]
end
t[i]={i,_labeled[f] or "?",math.floor((_telapsed[f]+dt)*1e6)/1e6,_ncalls[f],_defined[f]}
end
return t
end
local cols={3,20,8,6,32}
function profile.report(n)
local out={}
local report=profile.query(n)
for i,row in ipairs(report) do
for j=1,5 do
local s=tostring(row[j])
local l1,l2=#s,cols[j]
if l1<l2 then
s=s..(" "):rep(l2-l1)
elseif l1>l2 then
s=s:sub(l1-l2+1,l1)
end
row[j]=s
end
out[i]=table.concat(row," | ")
end
local row=" +-----+----------------------+----------+--------+----------------------------------+ \n"
local col=" | # | Function | Time | Calls | Code | \n"
local sz=row..col..row
if #out>0 then
sz=sz.." | "..table.concat(out," | \n | ").." | \n"
end
return "\n"..sz..row
end
local switch=false
function profile.switch()
switch=not switch
if not switch then
profile.stop()
love.system.setClipboardText(profile.report())
profile.reset()
return false
else
profile.start()
return true
end
end
-- store all internal profiler functions
for _,v in next,profile do
_internal[v]=type(v)=='function'
end
return profile

47
Zframework/require.lua Normal file
View File

@@ -0,0 +1,47 @@
package.cpath=package.cpath..';'..love.filesystem.getSaveDirectory()..'/lib/?.so;'..'?.dylib'
local loaded={}
local errorCount={}
return function(libName)
local require=require
local arch='unknown'
local success,res
if SYSTEM=='Web' then
return
end
if SYSTEM=='macOS' then
require=package.loadlib(libName..'.dylib','luaopen_'..libName)
success,res=pcall(require)
else
if SYSTEM=='Android' and not loaded[libName] then
local platform=(function()
local p=io.popen('uname -m')
arch=p:read('*a'):lower()
p:close()
if arch:find('v8') and not arch:find('v8l') or arch:find('64') then
return 'arm64-v8a'
else
return 'armeabi-v7a'
end
end)()
local data=love.filesystem.read('data','libAndroid/'..platform..'/'..libName..'.so')
if data then
love.filesystem.write('lib/'..libName..'.so',data)
end
loaded[libName]=true
end
success,res=pcall(require,libName)
end
if success and res then
return res
else
if not next(errorCount) then
MES.new('info',"Architecture: "..arch)
end
errorCount[libName]=(errorCount[libName] or 0)+1
if errorCount[libName]==1 then
MES.new('error',"Cannot load "..libName..": "..tostring(res):gsub('[\128-\255]+','??'))
else
MES.new('error',("Cannot load %s (x%d)"):format(libName,errorCount[libName]))
end
end
end

223
Zframework/scene.lua Normal file
View File

@@ -0,0 +1,223 @@
local scenes={}
local SCN={
mainTouchID=nil, -- First touching ID(userdata)
swapping=false, -- If Swapping
state={
tar=false, -- Swapping target
style=false, -- Swapping style
changeTime=false,-- Loading point
time=false, -- Full swap time
draw=false, -- Swap draw func
},
stack={},-- Scene stack
prev=false,
cur=false,
args={},-- Arguments from previous scene
scenes=scenes,
-- Events
update=false,
draw=false,
mouseClick=false,
touchClick=false,
mouseDown=false,
mouseMove=false,
mouseUp=false,
wheelMoved=false,
touchDown=false,
touchUp=false,
touchMove=false,
keyDown=false,
keyUp=false,
gamepadDown=false,
gamepadUp=false,
fileDropped=false,
directoryDropped=false,
resize=false,
}-- Scene datas, returned
function SCN.add(name,scene)
scenes[name]=scene
if scene.widgetList then
setmetatable(scene.widgetList,WIDGET.indexMeta)
end
end
function SCN.swapUpdate(dt)
local S=SCN.state
S.time=S.time-dt
if S.time<S.changeTime and S.time+dt>=S.changeTime then
-- Scene swapped this frame
SCN.stack[#SCN.stack]=S.tar
SCN.cur=S.tar
SCN.init(S.tar)
SCN.mainTouchID=nil
end
if S.time<0 then
SCN.swapping=false
end
end
function SCN.init(s)
love.keyboard.setTextInput(false)
local S=scenes[s]
WIDGET.setScrollHeight(S.widgetScrollHeight)
WIDGET.setWidgetList(S.widgetList)
SCN.enter=S.enter
SCN.leave=S.leave
SCN.mouseDown=S.mouseDown
SCN.mouseMove=S.mouseMove
SCN.mouseUp=S.mouseUp
SCN.mouseClick=S.mouseClick
SCN.wheelMoved=S.wheelMoved
SCN.touchDown=S.touchDown
SCN.touchUp=S.touchUp
SCN.touchMove=S.touchMove
SCN.touchClick=S.touchClick
SCN.keyDown=S.keyDown
SCN.keyUp=S.keyUp
SCN.gamepadDown=S.gamepadDown
SCN.gamepadUp=S.gamepadUp
SCN.fileDropped=S.fileDropped
SCN.directoryDropped=S.directoryDropped
SCN.resize=S.resize
SCN.update=S.update
SCN.draw=S.draw
if S.enter then
S.enter()
end
end
function SCN.push(tar)
table.insert(SCN.stack,tar or SCN.stack[#SCN.stack])
end
function SCN.pop()
table.remove(SCN.stack)
end
local swap={
none={
duration=0,changeTime=0,
draw=function() end
},
flash={
duration=.16,changeTime=.08,
draw=function() GC.clear(1,1,1) end
},
fade={
duration=.5,changeTime=.25,
draw=function(t)
t=t>.25 and 2-t*4 or t*4
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
fade_togame={
duration=2,changeTime=.5,
draw=function(t)
t=t>.5 and (2-t)/1.5 or t*.5
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
slowFade={
duration=3,changeTime=1.5,
draw=function(t)
t=t>1.5 and (3-t)/1.5 or t/1.5
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
swipeL={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(3-2*t)*2-1
GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h)
end
},
swipeR={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(2*t-3)*2+1
GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h)
end
},
swipeD={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(2*t-3)*2+1
GC.rectangle('fill',0,t*SCR.h,SCR.w,SCR.h)
end
},
}-- Scene swapping animations
function SCN.swapTo(tar,style,...)-- Parallel scene swapping, cannot back
if scenes[tar] then
if not SCN.swapping then
SCN.prev=SCN.stack[#SCN.stack]
style=style or 'fade'
SCN.swapping=true
SCN.args={...}
local S=SCN.state
S.tar,S.style=tar,style
S.time=swap[style].duration
S.changeTime=swap[style].changeTime
S.draw=swap[style].draw
end
else
MES.new('warn',"No Scene: "..tostring(tar))
end
end
function SCN.go(tar,style,...)-- Normal scene swapping, can back
if scenes[tar] then
if not SCN.swapping then
SCN.push(SCN.stack[#SCN.stack] or '_')
SCN.swapTo(tar,style,...)
end
else
MES.new('warn',"No Scene: "..tar)
end
end
function SCN.back(style,...)
if SCN.swapping then return end
-- Leave scene
if SCN.leave then
SCN.leave()
end
-- Poll&Back to previous Scene
if #SCN.stack>1 then
SCN.pop()
SCN.swapTo(SCN.stack[#SCN.stack],style,...)
else
SCN.swapTo('quit','slowFade')
end
end
function SCN.backTo(tar,style,...)
if SCN.swapping then return end
-- Leave scene
if SCN.leave then
SCN.leave()
end
-- Poll&Back to previous Scene
while SCN.stack[#SCN.stack]~=tar and #SCN.stack>1 do
SCN.pop()
end
SCN.swapTo(SCN.stack[#SCN.stack],style,...)
end
function SCN.printStack()
for i=0,#SCN.stack+1 do print(SCN.stack[i] or "-------") end
end
return SCN

73
Zframework/screen.lua Normal file
View File

@@ -0,0 +1,73 @@
local SCR={
w0=1280,h0=720, -- Default Screen Size
x=0,y=0, -- Up-left Coord on screen
cx=0,cy=0, -- Center Coord on screen (Center X/Y)
ex=0,ey=0, -- Down-right Coord on screen (End X/Y)
w=0,h=0, -- Fullscreen w/h for graphic functions
W=0,H=0, -- Fullscreen w/h for shader
safeX=0,safeY=0,-- Safe area
safeW=0,safeH=0,-- Safe area
rad=0, -- Radius
k=1, -- Scale size
dpi=1, -- DPI from gc.getDPIScale()
-- Screen transformation objects
origin=love.math.newTransform(),
xOy=love.math.newTransform(),
xOy_m=love.math.newTransform(),
xOy_ul=love.math.newTransform(),
xOy_u=love.math.newTransform(),
xOy_ur=love.math.newTransform(),
xOy_l=love.math.newTransform(),
xOy_r=love.math.newTransform(),
xOy_dl=love.math.newTransform(),
xOy_d=love.math.newTransform(),
xOy_dr=love.math.newTransform(),
}
function SCR.setSize(w,h)
SCR.w0,SCR.h0=w,h
end
function SCR.resize(w,h)
SCR.w,SCR.h,SCR.dpi=w,h,love.graphics.getDPIScale()
SCR.W,SCR.H=SCR.w*SCR.dpi,SCR.h*SCR.dpi
SCR.r=h/w
SCR.rad=(w^2+h^2)^.5
SCR.x,SCR.y=0,0
if SCR.r>=SCR.h0/SCR.w0 then
SCR.k=w/SCR.w0
SCR.y=(h-SCR.h0*SCR.k)/2
else
SCR.k=h/SCR.h0
SCR.x=(w-SCR.w0*SCR.k)/2
end
SCR.cx,SCR.cy=SCR.w/2,SCR.h/2
SCR.ex,SCR.ey=SCR.w-SCR.x,SCR.h-SCR.y
SCR.safeX,SCR.safeY,SCR.safeW,SCR.safeH=love.window.getSafeArea()
SCR.origin:setTransformation(0,0)
SCR.xOy:setTransformation(SCR.x,SCR.y,0,SCR.k)
SCR.xOy_m:setTransformation(w/2,h/2,0,SCR.k)
SCR.xOy_ul:setTransformation(0,0,0,SCR.k)
SCR.xOy_u:setTransformation(w/2,0,0,SCR.k)
SCR.xOy_ur:setTransformation(w,0,0,SCR.k)
SCR.xOy_l:setTransformation(0,h/2,0,SCR.k)
SCR.xOy_r:setTransformation(w,h/2,0,SCR.k)
SCR.xOy_dl:setTransformation(0,h,0,SCR.k)
SCR.xOy_d:setTransformation(w/2,h,0,SCR.k)
SCR.xOy_dr:setTransformation(w,h,0,SCR.k)
end
function SCR.info()
return {
("w0,h0 : %d, %d"):format(SCR.w0,SCR.h0),
("x,y : %d, %d"):format(SCR.x,SCR.y),
("cx,cy : %d, %d"):format(SCR.cx,SCR.cy),
("ex,ey : %d, %d"):format(SCR.ex,SCR.ey),
("w,h : %d, %d"):format(SCR.w,SCR.h),
("W,H : %d, %d"):format(SCR.W,SCR.H),
("safeX,safeY : %d, %d"):format(SCR.safeX,SCR.safeY),
("safeW,safeH : %d, %d"):format(SCR.safeW,SCR.safeH),
("k,dpi,rad : %.2f, %d, %.2f"):format(SCR.k,SCR.dpi,SCR.rad),
}
end
return SCR

169
Zframework/sfx.lua Normal file
View File

@@ -0,0 +1,169 @@
local type,rem=type,table.remove
local floor,rnd=math.floor,math.random
local sfxList={}
local packSetting={}
local Sources={}
local volume=1
local stereo=1
local noteVal={
C=1,c=1,
D=3,d=3,
E=5,e=5,
F=6,f=6,
G=8,g=8,
A=10,a=10,
B=12,b=12,
}
local noteName={'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}
local function _getTuneHeight(tune)
local octave=tonumber(tune:sub(-1,-1))
if octave then
local tuneHeight=noteVal[tune:sub(1,1)]
if tuneHeight then
tuneHeight=tuneHeight+(octave-1)*12
local s=tune:sub(2,2)
if s=='s' or s=='#' then
tuneHeight=tuneHeight+1
elseif s=='f' or s=='b' then
tuneHeight=tuneHeight-1
end
return tuneHeight
end
end
end
local SFX={}
function SFX.init(list)
assert(type(list)=='table',"Initialize SFX lib with a list of filenames!")
for i=1,#list do table.insert(sfxList,list[i]) end
end
function SFX.load(path)
local c=0
local missing=0
for i=1,#sfxList do
local fullPath=path..sfxList[i]..'.ogg'
if love.filesystem.getInfo(fullPath) then
if Sources[sfxList[i]] then
for j=1,#Sources[sfxList[i]] do
Sources[sfxList[i]][j]:release()
end
end
Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')}
c=c+1
else
LOG("No SFX: "..sfxList[i]..'.ogg',.1)
missing=missing+1
end
end
LOG(c.."/"..#sfxList.." SFX files loaded")
LOG(missing.." SFX files missing")
if missing>0 then
MES.new('info',missing.." SFX files missing")
end
collectgarbage()
end
function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
assert(pack.name,"No field: name")
assert(pack.path,"No field: path")
local num=1
while love.filesystem.getInfo(pack.path..'/'..num..'.ogg') do
Sources[pack.name..num]={love.audio.newSource(pack.path..'/'..num..'.ogg','static')}
num=num+1
end
local base=(_getTuneHeight(pack.base) or 37)-1
local top=base+num-1
packSetting[pack.name]={base=base,top=top}
LOG((num-1).." "..pack.name.." samples loaded")
end
function SFX.getCount()
return #sfxList
end
function SFX.setVol(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume')
volume=v
end
function SFX.setStereo(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong stereo')
stereo=v
end
function SFX.getNoteName(note)
if note<1 then
return '---'
else
note=note-1
local octave=floor(note/12)+1
return noteName[note%12+1]..octave
end
end
function SFX.playSample(pack,...)-- vol-1, sampSet1, vol-2, sampSet2
if ... then
local arg={...}
local vol
for i=1,#arg do
local a=arg[i]
if type(a)=='number' and a<=1 then
vol=a
else
local base=packSetting[pack].base
local top=packSetting[pack].top
local tune=type(a)=='string' and _getTuneHeight(a) or a-- Absolute tune in number
local playTune=tune+rnd(-2,2)
if playTune<=base then-- Too low notes
playTune=base+1
elseif playTune>top then-- Too high notes
playTune=top
end
SFX.play(pack..playTune-base,vol,nil,tune-playTune)
end
end
end
end
local function _play(name,vol,pos,pitch)
if volume==0 or vol==0 then return end
local S=Sources[name]-- Source list
if not S then return end
local n=1
while S[n]:isPlaying() do
n=n+1
if not S[n] then
S[n]=S[1]:clone()
S[n]:seek(0)
break
end
end
S=S[n]-- AU_SRC
if S:getChannelCount()==1 then
if pos then
pos=MATH.clamp(pos,-1,1)*stereo
S:setPosition(pos,1-pos^2,0)
else
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:setPitch(pitch and 1.0594630943592953^pitch or 1)
S:play()
end
SFX.fplay=_play-- Play sounds without apply module's volume setting
function SFX.play(name,vol,pos,pitch)
_play(name,(vol or 1)*volume,pos,pitch)
end
function SFX.reset()
for _,L in next,Sources do
if type(L)=='table' then
for i=#L,1,-1 do
if not L[i]:isPlaying() then
rem(L,i)
end
end
end
end
end
return SFX

5675
Zframework/sha2.lua Normal file

File diff suppressed because it is too large Load Diff

423
Zframework/stringExtend.lua Normal file
View File

@@ -0,0 +1,423 @@
local data=love.data
local STRING={}
local assert,tostring,tonumber=assert,tostring,tonumber
local floorint,format=math.floor,string.format
local find,sub,gsub=string.find,string.sub,string.gsub
local rep,upper=string.rep,string.upper
local char,byte=string.char,string.byte
-- "Replace dollars", replace all $n with ...
function STRING.repD(str,...)
local l={...}
for i=#l,1,-1 do
str=gsub(str,'$'..i,l[i])
end
return str
end
-- "Scan arg", scan if str has the arg (format of str is like "-json -q", arg is like "-q")
function STRING.sArg(str,switch)
if find(str.." ",switch.." ") then
return true
end
end
do-- function STRING.shiftChar(c)
local shiftMap={
['1']='!',['2']='@',['3']='#',['4']='$',['5']='%',
['6']='^',['7']='&',['8']='*',['9']='(',['0']=')',
['`']='~',['-']='_',['=']='+',
['[']='{',[']']='}',['\\']='|',
[';']=':',['\'']='"',
[',']='<',['.']='>',['/']='?',
}
function STRING.shiftChar(c)
return shiftMap[c] or upper(c)
end
end
local upperData,lowerData
function STRING.upperUTF8(str)
for _,pair in next,upperData do
str=str:gsub(pair[1],pair[2])
end
return str
end
function STRING.lowerUTF8(str)
for _,pair in next,lowerData do
str=str:gsub(pair[1],pair[2])
end
return str
end
function STRING.trim(s)
if not s:find("%S") then return "" end
s=s:sub((s:find("%S"))):reverse()
return s:sub((s:find("%S"))):reverse()
end
function STRING.split(s,sep,regex)
local L={}
local p1,p2=1-- start,target
if regex then
while p1<=#s do
p2=find(s,sep,p1) or #s+1
L[#L+1]=sub(s,p1,p2-1)
p1=p2+#sep
end
else
while p1<=#s do
p2=find(s,sep,p1,true) or #s+1
L[#L+1]=sub(s,p1,p2-1)
p1=p2+#sep
end
end
return L
end
function STRING.simpEmailCheck(e)
e=STRING.split(e,"@")
if #e~=2 then return false end
if e[1]:sub(-1)=="." or e[2]:sub(-1)=="." then return false end
local e1,e2=STRING.split(e[1],"."),STRING.split(e[2],".")
if #e1*#e2==0 then return false end
for _,v in next,e1 do if #v==0 then return false end end
for _,v in next,e2 do if #v==0 then return false end end
return true
end
local MINUTE=60
local HOUR=3600
local DAY=86400
local YEAR=31536000 -- 365 days
local function convertSecondsToUnits(t) -- convert seconds to {seconds, minutes, hours, days, years}
local years=floorint(t/YEAR)
local remainder=t%YEAR
local days=floorint(remainder/DAY)
remainder=remainder%DAY
local hours=floorint(remainder/HOUR)
remainder=remainder%HOUR
local minutes=floorint(remainder/MINUTE)
local seconds=remainder%MINUTE
return seconds,minutes,hours,days,years
end
-- MM:SS
function STRING.time_simp(t)
return format("%02d:%02d",floorint(t/MINUTE),floorint(t%MINUTE))
end
local timeLetters={' y',' d',' h',' m',' s',' ms'}
-- Display 2 largest units of time.
function STRING.time_short(t)
-- Early returns to prevent nil values
if t<0 then return '-'..STRING.time_short(-t) end -- negative time
if t<1 then return math.floor(t*1000)..timeLetters[6] end -- 123 ms
if t<MINUTE then return math.floor(t)..timeLetters[5]..' '..math.floor((t%1)*1000)..timeLetters[6] end -- 12s 345ms
local timeUnits=TABLE.reverse({convertSecondsToUnits(t)})
-- floor seconds
timeUnits[#timeUnits]=floorint(timeUnits[#timeUnits])
local outputStr=''
for i=1,#timeUnits do
if timeUnits>0 then
return timeUnits[i]..timeLetters[i]..' '..timeUnits[i+1]..timeLetters[i+1]
end
end
end
function STRING.time(t)
local s,m,h,d,y=convertSecondsToUnits(t)
if t<MINUTE then
return format("%.3f″",t) -- example: 12.345″
elseif t<HOUR then
return format("%d%05.2f″",m,s) -- 123.45″
elseif t<DAY then
return format("%d:%.2d%04.1f″",h,m,s) -- 12:3456.7″
elseif t<YEAR then
return format("%dd %d:%.2d%.2d″",d,h,m,s) -- 123d 12:3456″
else
return format("%dy %dd %d:%.2d",y,d,h,m) -- 1y 234d 12:34
end
end
function STRING.time_ext(t)
local s,m,h,d,y=convertSecondsToUnits(t)
if t<MINUTE then
return format("%.5f″",t) -- 12.34567″
elseif t<HOUR then
return format("%d%06.3f″",m,s) -- 123.456″
elseif t<DAY then
return format("%d:%.2d%05.2f″",h,m,s) -- 12:3456.78″
elseif t<YEAR then
return format("%dd %d:%.2d%04.1f″",d,h,m,s) -- 123d 12:3456.7″
else
return format("%dy %dd %d:%.2d%.2d″",y,d,h,m,s) -- 1y 234d 12:3456″
end
end
function STRING.UTF8(n)-- Simple utf8 coding
assert(type(n)=='number',"Wrong type ("..type(n)..")")
assert(n>=0 and n<2^31,"Out of range ("..n..")")
if n<2^7 then return char(n)
elseif n<2^11 then return char(192+floorint(n/2^06),128+n%2^6)
elseif n<2^16 then return char(224+floorint(n/2^12),128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^21 then return char(240+floorint(n/2^18),128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^26 then return char(248+floorint(n/2^24),128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^31 then return char(252+floorint(n/2^30),128+floorint(n/2^24)%2^6,128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
end
end
do-- functions to shorted big numbers
local lg=math.log10
local units={"","K","M","B","T","Qa","Qt","Sx","Sp","Oc","No"}
local preUnits={"","U","D","T","Qa","Qt","Sx","Sp","O","N"}
local secUnits={"Dc","Vg","Tg","Qd","Qi","Se","St","Og","Nn","Ce"}-- Ce is next-level unit, but DcCe is not used so used here
for _,preU in next,preUnits do for _,secU in next,secUnits do table.insert(units,preU..secU) end end
function STRING.bigInt(t)
if t<1000 then
return tostring(t)
elseif t~=1e999 then
local e=floorint(lg(t)/3)
return(t/10^(e*3))..units[e+1]
else
return "INF"
end
end
local MIN_SI=-30 -- current lowest order of magnitude for SI units (quecto-; 10^-30)
local MAX_SI=30 -- current highest order of magnitude for SI units (quetta-; 10^30)
local SI_SHORT={
[-30]='q',[-27]='r',[-24]='y',[-21]='z',[-18]='a',[-15]='f',[-12]='p',
[-9]='n',[-6]='μ',[-3]='m',[0]='',[3]='k',[6]='M',[9]='G',
[12]='T',[15]='P',[18]='E',[21]='Z',[24]='Y',[27]='R',[30]='Q'
}
local SI_LONG={
[-30]='quecto',[-27]='ronto',[-24]='yocto',[-21]='zepto',[-18]='atto',[-15]='femto',[-12]='pico',
[-9]='nano',[-6]='micro',[-3]='milli',[0]='',[3]='kilo',[6]='mega',[9]='giga',
[12]='tera',[15]='peta',[18]='exa',[21]='zetta',[24]='yotta',[27]='ronna',[30]='quetta'
}
--[[
Converts a number into SI notation with letter prefixes.
NOTE: Only power-of-thousand prefixes; no deci-/centi-.
Arguments:
- num: The number to be converted to SI notation.
- unit: [optional] The unit to be concatenated at the end.
Example: STRING.SI(10^-9,"m") --> "1 nm"
]]
function STRING.SI(num, unit)
unit=unit or ''
local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI)
local prefix=SI_SHORT[order]
local scaledNum=num/10^order
local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','')
return formattedNum.." "..prefix..unit
end
--[[
Converts a number into SI notation with word prefixes.
NOTE: Only power-of-thousand prefixes; no deci-/centi-.
Arguments:
- num: The number to be converted to SI notation.
- unit: [optional] The unit to be concatenated at the end.
Example: STRING.SI(10^9,"hertz") --> "1 megahertz"
]]
function STRING.SILong(num, unit)
unit=unit or ''
local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI)
local prefix=SI_LONG[order]
local scaledNum=num/10^order
local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','')
return formattedNum.." "..prefix..unit
end
end
do-- function STRING.toBin, STRING.toOct, STRING.toHex(n,len)
function STRING.toBin(n,len)
local s=""
while n>0 do
s=(n%2)..s
n=floorint(n/2)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
function STRING.toOct(n,len)
local s=""
while n>0 do
s=(n%8)..s
n=floorint(n/8)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
local b16={[0]='0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}
function STRING.toHex(n,len)
local s=""
while n>0 do
s=b16[n%16]..s
n=floorint(n/16)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
end
function STRING.hexColor(str)--[LOW PERFORMENCE]
assert(type(str)=='string')
if str:sub(1,1)=="#" then str=str:sub(2) end
assert(#str<=8)
local r=(tonumber(str:sub(1,2),16) or 0)/255
local g=(tonumber(str:sub(3,4),16) or 0)/255
local b=(tonumber(str:sub(5,6),16) or 0)/255
local a=(tonumber(str:sub(7,8),16) or 255)/255
return r,g,b,a
end
do-- function STRING.urlEncode(s)
local rshift=bit.rshift
local b16={[0]='0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}
function STRING.urlEncode(s)
local out=""
for i=1,#s do
if s:sub(i,i):match("[a-zA-Z0-9]") then
out=out..s:sub(i,i)
else
local b=s:byte(i)
out=out.."%"..b16[rshift(b,4)]..b16[b%16]
end
end
return out
end
end
function STRING.vcsEncrypt(text,key)
local keyLen=#key
local result=""
local buffer=""
for i=0,#text-1 do
buffer=buffer..char((byte(text,i+1)-32+byte(key,i%keyLen+1))%95+32)
if #buffer==26 then
result=result..buffer
buffer=""
end
end
return result..buffer
end
function STRING.vcsDecrypt(text,key)
local keyLen=#key
local result=""
local buffer=""
for i=0,#text-1 do
buffer=buffer..char((byte(text,i+1)-32-byte(key,i%keyLen+1))%95+32)
if #buffer==26 then
result=result..buffer
buffer=""
end
end
return result..buffer
end
function STRING.digezt(text)-- Not powerful hash, just protect the original text
local out={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local seed=26
for i=1,#text do
local c=byte(text,i)
seed=(seed+c)%26
c=c+seed
local pos=c*i%16
local step=(c+i)%4+1
local times=2+(c%6)
for _=1,times do
out[pos+1]=(out[pos+1]+c)%256
pos=(pos+step)%16
end
end
local result=""
for i=1,16 do result=result..char(out[i]) end
return result
end
function STRING.readLine(str)
local p=str:find("\n")
if p then
return str:sub(1,p-1),str:sub(p+1)
else
return str,""
end
end
function STRING.readChars(str,n)
return sub(str,1,n),sub(str,n+1)
end
function STRING.packBin(s)
return data.encode('string','base64',data.compress('string','zlib',s))
end
function STRING.unpackBin(str)
local res
res,str=pcall(data.decode,'string','base64',str)
if not res then return end
res,str=pcall(data.decompress,'string','zlib',str)
if res then return str end
end
function STRING.packText(s)
return data.encode('string','base64',data.compress('string','gzip',s))
end
function STRING.unpackText(str)
local res
res,str=pcall(data.decode,'string','base64',str)
if not res then return end
res,str=pcall(data.decompress,'string','gzip',str)
if res then return str end
end
function STRING.packTable(t)
return STRING.packText(JSON.encode(t))
end
function STRING.unpackTable(t)
return JSON.decode(STRING.unpackText(t))
end
repeat
if love and love.filesystem and type(love.filesystem.read)=='function' then
upperData=love.filesystem.read('Zframework/upcaser.txt')
lowerData=love.filesystem.read('Zframework/lowcaser.txt')
else
local f=io.open('Zframework/upcaser.txt','r')
if not f then break end
upperData=f:read('a')
f:close()
f=io.open('Zframework/lowcaser.txt','r')
if not f then break end
lowerData=f:read('a')
f:close()
end
upperData=STRING.split(gsub(upperData,'\n',','),',')
lowerData=STRING.split(gsub(lowerData,'\n',','),',')
for i=1,#upperData do
local pair=STRING.split(upperData[i],'=')
-- upperData[pair[1]]=pair[2]
upperData[i]=pair
end
for i=1,#lowerData do
local pair=STRING.split(lowerData[i],'=')
-- lowerData[pair[1]]=pair[2]
lowerData[i]=pair
end
until true
return STRING

205
Zframework/sysFX.lua Normal file
View File

@@ -0,0 +1,205 @@
local gc=love.graphics
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
local gc_draw,gc_line=gc.draw,gc.line
local gc_rectangle,gc_circle=gc.rectangle,gc.circle
local max,min=math.max,math.min
local rnd=math.random
local ins,rem=table.insert,table.remove
local fx={}
local function _normUpdate(S,dt)
S.t=S.t+dt*S.rate
return S.t>1
end
local FXupdate={}
function FXupdate.badge(S,dt)
S.t=S.t+dt
if S.t<.2 then
S.x,S.y=S.x1-14,S.y1-14
elseif S.t<.8 then
local t=((S.t-.2)*1.6667)
t=(3-2*t)*t*t
S.x,S.y=S.x1*(1-t)+S.x2*t-14,S.y1*(1-t)+S.y2*t-14
else
S.x,S.y=S.x2-14,S.y2-14
end
return S.t>=1
end
FXupdate.attack=_normUpdate
FXupdate.tap=_normUpdate
FXupdate.ripple=_normUpdate
FXupdate.rectRipple=_normUpdate
FXupdate.shade=_normUpdate
function FXupdate.cell(S,dt)
if S.vx then
S.x=S.x+S.vx*S.rate
S.y=S.y+S.vy*S.rate
if S.ax then
S.vx=S.vx+S.ax*S.rate
S.vy=S.vy+S.ay*S.rate
end
end
S.t=S.t+dt*S.rate
return S.t>1
end
FXupdate.line=_normUpdate
local FXdraw={}
function FXdraw.badge(S)
gc_setColor(1,1,1,S.t<.2 and S.t*.6 or S.t<.8 and 1 or (1-S.t)*.6)
gc_draw(IMG.badgeIcon,S.x,S.y)
end
function FXdraw.attack(S)
gc_setColor(S.r*2,S.g*2,S.b*2,S.a*min(4-S.t*4,1))
gc_setLineWidth(S.wid)
local t1,t2=max(5*S.t-4,0),min(S.t*4,1)
gc_line(
S.x1*(1-t1)+S.x2*t1,
S.y1*(1-t1)+S.y2*t1,
S.x1*(1-t2)+S.x2*t2,
S.y1*(1-t2)+S.y2*t2
)
gc_setLineWidth(S.wid*.6)
t1,t2=max(4*S.t-3,0),min(S.t*5,1)
gc_line(
S.x1*(1-t1)+S.x2*t1,
S.y1*(1-t1)+S.y2*t1,
S.x1*(1-t2)+S.x2*t2,
S.y1*(1-t2)+S.y2*t2
)
end
function FXdraw.tap(S)
local t=S.t
gc_setColor(1,1,1,(1-t)*.4)
gc_circle('fill',S.x,S.y,30*(1-t)^.5)
end
function FXdraw.ripple(S)
local t=S.t
gc_setLineWidth(2)
gc_setColor(1,1,1,1-t)
gc_circle('line',S.x,S.y,t*(2-t)*S.r)
end
function FXdraw.rectRipple(S)
gc_setLineWidth(6)
gc_setColor(1,1,1,1-S.t)
local r=(10*S.t)^1.2
gc_rectangle('line',S.x-r,S.y-r,S.w+2*r,S.h+2*r)
end
function FXdraw.shade(S)
gc_setColor(S.r,S.g,S.b,1-S.t)
gc_rectangle('fill',S.x,S.y,S.w,S.h,2)
end
function FXdraw.cell(S)
gc_setColor(1,1,1,1-S.t)
gc_draw(S.image,S.x,S.y,nil,S.size,nil,S.cx,S.cy)
end
function FXdraw.line(S)
gc_setColor(1,1,1,S.a*(1-S.t))
gc_line(S.x1,S.y1,S.x2,S.y2)
end
local SYSFX={}
function SYSFX.update(dt)
for i=#fx,1,-1 do
if fx[i]:update(dt) then
rem(fx,i)
end
end
end
function SYSFX.draw()
for i=1,#fx do
fx[i]:draw()
end
end
function SYSFX.newBadge(x1,y1,x2,y2)
ins(fx,{
update=FXupdate.badge,
draw=FXdraw.badge,
t=0,
x=x1,y=y1,
x1=x1,y1=y1,
x2=x2,y2=y2,
})
end
function SYSFX.newAttack(rate,x1,y1,x2,y2,wid,r,g,b,a)
ins(fx,{
update=FXupdate.attack,
draw=FXdraw.attack,
t=0,
rate=rate,
x1=x1,y1=y1,-- Start pos
x2=x2,y2=y2,-- End pos
wid=wid,-- Line width
r=r,g=g,b=b,a=a,
})
end
function SYSFX.newTap(rate,x,y)
local T=
{
update=FXupdate.tap,
draw=FXdraw.tap,
t=0,
rate=rate,
x=x,y=y,
}
ins(fx,T)
end
function SYSFX.newRipple(rate,x,y,r)
ins(fx,{
update=FXupdate.ripple,
draw=FXdraw.ripple,
t=0,
rate=rate,
x=x,y=y,r=r,
})
end
function SYSFX.newRectRipple(rate,x,y,w,h)
ins(fx,{
update=FXupdate.rectRipple,
draw=FXdraw.rectRipple,
t=0,
rate=rate,
x=x,y=y,w=w,h=h,
})
end
function SYSFX.newShade(rate,x,y,w,h,r,g,b)
ins(fx,{
update=FXupdate.shade,
draw=FXdraw.shade,
t=0,
rate=rate,
x=x,y=y,w=w,h=h,
r=r or 1,g=g or 1,b=b or 1,
})
end
function SYSFX.newCell(rate,image,size,x,y,vx,vy,ax,ay)
ins(fx,{
update=FXupdate.cell,
draw=FXdraw.cell,
t=0,
rate=rate*(.9+rnd()*.2),
image=image,size=size,
cx=image:getWidth()*.5,cy=image:getHeight()*.5,
x=x,y=y,
vx=vx,vy=vy,
ax=ax,ay=ay,
})
end
function SYSFX.newLine(rate,x1,y1,x2,y2,r,g,b,a)
ins(fx,{
update=FXupdate.line,
draw=FXdraw.line,
t=0,
rate=rate,
x1=x1 or 0,y1=y1 or 0,
x2=x2 or x1 or 1280,y2=y2 or y1 or 720,
r=r or 1,g=g or 1,b=b or 1,a=a or 1,
})
end
return SYSFX

463
Zframework/tableExtend.lua Normal file
View File

@@ -0,0 +1,463 @@
local rnd=math.random
local min,max=math.min,math.max
local find=string.find
local ins,rem=table.insert,table.remove
local next,type=next,type
local TABLE={}
-----------------------[Making Tables]------------------------
-- Get a new filled table
function TABLE.new(val,count)
local L={}
for i=1,count do
L[i]=val
end
return L
end
-- Get a copy of [1~#] elements
function TABLE.shift(org,depth)
if not depth then depth=1e99 end
local L={}
for i=1,#org do
if type(org[i])=='table' and depth>0 then
L[i]=TABLE.shift(org[i],depth-1)
else
L[i]=org[i]
end
end
return L
end
-- Get a full copy of a table, depth=how many layers will be recreate, default to inf
function TABLE.copy(org,depth)
if not depth then depth=1e99 end
local L={}
for k,v in next,org do
if type(v)=='table' and depth>0 then
L[k]=TABLE.copy(v,depth-1)
else
L[k]=v
end
end
return L
end
-- Connect [1~#] elements of new to the end of org
function TABLE.connect(org,new)
local l0=#org
for i=1,#new do
org[l0+i]=new[i]
end
return org
end
-- Get a table of two lists connected
function TABLE.combine(L1,L2)
local l={}
local l0=#L1
for i=1,l0 do l[i]=L1[i] end
for i=1,#L2 do l[l0+i]=L2[i] end
return l
end
----------------------[Modifying Tables]----------------------
-- For all things in new, push to old
function TABLE.cover(new,old)
for k,v in next,new do
old[k]=v
end
end
-- For all things in new, push to old
function TABLE.coverR(new,old)
for k,v in next,new do
if type(v)=='table' and type(old[k])=='table' then
TABLE.coverR(v,old[k])
else
old[k]=v
end
end
end
-- For all things in org, delete them if it's in sub
function TABLE.subtract(org,sub)
for _,v in next,sub do
while true do
local p=TABLE.search(org,v)
if p then
rem(org,p)
else
break
end
end
end
end
-- For all things in new if same type in old, push to old
function TABLE.update(new,old)
for k,v in next,new do
if type(v)==type(old[k]) then
if type(v)=='table' then
TABLE.update(v,old[k])
else
old[k]=v
end
end
end
end
-- For all things in new if no val in old, push to old
function TABLE.complete(new,old)
for k,v in next,new do
if type(v)=='table' then
if old[k]==nil then old[k]={} end
TABLE.complete(v,old[k])
elseif old[k]==nil then
old[k]=v
end
end
end
-------------------[Removing Table Values]--------------------
-- Pop & return random [1~#] of table
function TABLE.popRandom(t)
local l=#t
if l>0 then
local r=rnd(l)
r,t[r]=t[r],t[l]
t[l]=nil
return r
end
end
-- Remove [1~#] of table
function TABLE.cut(G)
for i=1,#G do
G[i]=nil
end
end
-- Clear table
function TABLE.clear(G)
for k in next,G do
G[k]=nil
end
end
--------------------[Handling duplicates]---------------------
-- Remove duplicated value of [1~#]
function TABLE.trimDuplicate(org)
local cache={}
for i=1,#org,-1 do
if cache[org[i]] then
rem(org,i)
else
cache[org[i]]=true
end
end
end
-- Discard duplicated value
function TABLE.remDuplicate(org)
local cache={}
for k,v in next,org do
if cache[v] then
org[k]=nil
else
cache[v]=true
end
end
end
--[[
Run length encoder. Input must be a list containing non-nil value(s).
Example:
- Input: {1, 1, 2, 2, 2, 1}
- Output: {{1, 2}, {2, 3}, {1, 1}}
- This means: "Two 1's in a row", "Three 2's in a row", "One 1 in a row"
]]
function TABLE.RLE(org)
local output={}
local cur=nil
local count=0
for i=1,#org do
local item=org[i]
if item==cur then
count=count+1
else
if cur then
ins(output,{cur,count})
end
cur=item
count=1
end
end
if cur then
ins(output,{cur,count})
end
return output
end
----------------------[Reversing Tables]----------------------
-- Reverse [1~#]
function TABLE.reverse(org)
local l=#org
for i=1,math.floor(l/2) do
org[i],org[l+1-i]=org[l+1-i],org[i]
end
end
----------------------[Table Comparison]----------------------
-- Check if tow list have same elements
function TABLE.compare(a,b)
if #a~=#b then return false end
if a==b then return true end
for i=1,#a do
if a[i]~=b[i] then return false end
end
return true
end
-- Check if tow table have same elements
function TABLE.equal(a,b)
if #a~=#b then return false end
if a==b then return true end
for k,v in next,a do
if b[k]~=v then return false end
end
return true
end
----------------------[Table Operations]----------------------
-- Find value in [1~#], like string.find
function TABLE.find(t,val,start)
for i=start or 1,#t do if t[i]==val then return i end end
end
-- Get subset of table, like string.sub
function TABLE.sub(t,i,j)
local subTable={}
for k=max(i,1),(j and min(j,#t) or #t) do
subTable[k-i+1]=t[k]
end
return subTable
end
-- Replace value in [1~#], like string.gsub
function TABLE.gsub(t,v_old,v_new,count,start)
if not start then start=1 end
if not count then count=1e99 end
while t[start] and count>0 do
if t[start]==v_old then
t[start]=v_new
count=count-1
end
end
end
-- Return next value of [1~#] (by value)
function TABLE.next(t,val)
for i=1,#t do if t[i]==val then return t[i%#t+1] end end
end
-- Find value in whole table
function TABLE.search(t,val)
for k,v in next,t do if v==val then return k end end
end
-- Replace all value in t
function TABLE.replace(t,v_old,v_new)
for k,v in next,t do
if v==v_old then
t[k]=v_new
end
end
end
-- Re-index string value of a table
function TABLE.reIndex(org)
for k,v in next,org do
if type(v)=='string' then
org[k]=org[v]
end
end
end
-- Return table where keys and values are swapped (useful for hashmap)
function TABLE.kvSwap(t)
local output={}
for k,v in next,t do output[v]=k end
return output
end
--[[
Extracts a value from each sub-table in table.
Example input: ({{name='A'},{name='B'},{name='C'}}, 'name')
Output: {'A','B'}
]]
function TABLE.extract(t,keyName)
local output={}
for k,v in next,t do output[k]=v[keyName] end
return output
end
-- Get element count of table
function TABLE.getSize(t)
local size=0
for _ in next,t do size=size+1 end
return size
end
-----------------------[Table Rotation]-----------------------
-- Copy a rotated matrix table
function TABLE.rotate(cb,dir)
local icb={}
if dir=='R' then-- Rotate CW
for y=1,#cb[1] do
icb[y]={}
for x=1,#cb do
icb[y][x]=cb[x][#cb[1]-y+1]
end
end
elseif dir=='L' then-- Rotate CCW
for y=1,#cb[1] do
icb[y]={}
for x=1,#cb do
icb[y][x]=cb[#cb-x+1][y]
end
end
elseif dir=='F' then-- Rotate 180 degree
for y=1,#cb do
icb[y]={}
for x=1,#cb[1] do
icb[y][x]=cb[#cb-y+1][#cb[1]-x+1]
end
end
end
return icb
end
----------------------[Table Functions]-----------------------
-- Return a function that return a value of table
function TABLE.func_getVal(t,k)
return function() return t[k] end
end
-- Return a function that reverse a value of table
function TABLE.func_revVal(t,k)
return function() t[k]=not t[k] end
end
-- Return a function that set a value of table
function TABLE.func_setVal(t,k)
return function(v) t[k]=v end
end
-------------------------[Table Dump]-------------------------
-- Dump a simple lua table (no whitespaces)
do-- function TABLE.dumpDeflate(L,t)
local function dump(L)
if type(L)~='table' then return end
local s='{'
local count=1
for k,v in next,L do
local T=type(k)
if T=='number' then
if k==count then
k=''
count=count+1
else
k='['..k..']='
end
elseif T=='string' then
if find(k,'[^0-9a-zA-Z_]') then
k='[\''..k..'\']='
else
k=k..'='
end
elseif T=='boolean' then k='['..k..']='
else error("Error key type!")
end
T=type(v)
if T=='number' then v=tostring(v)
elseif T=='string' then v='\''..v..'\''
elseif T=='table' then v=dump(v)
elseif T=='boolean' then v=tostring(v)
else v='*'..tostring(v)
end
s=s..k..v..','
end
return s..'}'
end
TABLE.dumpDeflate=dump
end
-- Dump a simple lua table
do-- function TABLE.dump(L,t)
local tabs=setmetatable({
[0]='',
'\t',
},{__index=function(self,k)
if k>=626 then error("Too many tabs!") end
for i=#self+1,k do
self[i]=self[i-1]..'\t'
end
return self[k]
end})
local function dump(L,t)
local s='{\n'
if not t then
s='return {\n'
t=1
if type(L)~='table' then
return
end
end
local count=1
for k,v in next,L do
local T=type(k)
if T=='number' then
if k==count then
k=''
count=count+1
else
k='['..k..']='
end
elseif T=='string' then
if find(k,'[^0-9a-zA-Z_]') then
k='[\''..k..'\']='
else
k=k..'='
end
elseif T=='boolean' then k='['..k..']='
else k='[\'*'..tostring(k)..'\']='
end
T=type(v)
if T=='number' then v=tostring(v)
elseif T=='string' then v='\''..v..'\''
elseif T=='table' then v=dump(v,t+1)
elseif T=='boolean' then v=tostring(v)
else v='*'..tostring(v)
end
s=s..tabs[t]..k..v..',\n'
end
return s..tabs[t-1]..'}'
end
TABLE.dump=dump
end
return TABLE

83
Zframework/task.lua Normal file
View File

@@ -0,0 +1,83 @@
local rem=table.remove
local assert,resume,status=assert,coroutine.resume,coroutine.status
local rawset=rawset
local timer=love.timer.getTime
local TASK={}
-- Locks
local locks=setmetatable({},{
__index=function(self,k) rawset(self,k,-1e99)return -1e99 end,
__newindex=function(self,k) rawset(self,k,-1e99) end,
})
function TASK.lock(name,T)
if timer()>=locks[name] then
locks[name]=timer()+(T or 1e99)
return true
else
return false
end
end
function TASK.unlock(name)
locks[name]=-1e99
end
function TASK.getLock(name)
local v=locks[name]-timer()
return v>0 and v
end
function TASK.clearLock()
for k in next,locks do
locks[k]=nil
end
end
local tasks={}
function TASK.getCount()
return #tasks
end
local trigFrame=0
function TASK.update(dt)
trigFrame=trigFrame+dt*60
for _=1,trigFrame do
for i=#tasks,1,-1 do
local T=tasks[i]
if status(T.thread)=='dead' then
rem(tasks,i)
else
assert(resume(T.thread,dt/trigFrame))
end
end
end
trigFrame=1
end
function TASK.new(code,...)
local thread=coroutine.create(code)
assert(resume(thread,...))
if status(thread)~='dead' then
tasks[#tasks+1]={
thread=thread,
code=code,
args={...},
}
end
end
function TASK.removeTask_code(code)
for i=#tasks,1,-1 do
if tasks[i].code==code then
rem(tasks,i)
end
end
end
function TASK.removeTask_iterate(func,...)
for i=#tasks,1,-1 do
if func(tasks[i],...) then
rem(tasks,i)
end
end
end
function TASK.clear()
TABLE.cut(tasks)
end
return TASK

18
Zframework/test.lua Normal file
View File

@@ -0,0 +1,18 @@
local yield=coroutine.yield
local TEST={}
-- Wait for the scene swapping animation to finish
function TEST.yieldUntilNextScene()
while SCN.swapping do yield() end
end
function TEST.yieldN(frames)
for _=1,frames do yield() end
end
function TEST.yieldT(timeout)
local t=0
repeat t=t+yield() until t>=timeout
end
return TEST

152
Zframework/text.lua Normal file
View File

@@ -0,0 +1,152 @@
local getColor,setColor=GC.getColor,GC.setColor
local floor,rnd=math.floor,math.random
local ins,rem=table.insert,table.remove
local draw=GC.draw
local texts={}
local textFX={}
function textFX.appear(t)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.sudden(t)
setColor(1,1,1,1-t.c)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.fly(t)
draw(
t.text,t.x+(t.c-.5)^3*300,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.stretch(t)
draw(
t.text,t.x,t.y,
nil,
t.c<.3 and (.3-t.c)*1.6+1 or 1,1,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.drive(t)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5,
t.c<.3 and (.3-t.c)*2 or 0,0
)
end
function textFX.spin(t)
draw(
t.text,t.x,t.y,
t.c<.3 and (.3-t.c)^2*4 or t.c<.8 and 0 or (t.c-.8)^2*-4,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.flicker(t)
local _,_,_,T=getColor()
setColor(1,1,1,T*(rnd()+.5))
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.zoomout(t)
draw(
t.text,t.x,t.y,
nil,
t.c^.5*.1+1,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.beat(t)
local k=t.c<.3 and 1.3-t.c^2/.3 or 1
draw(
t.text,t.x,t.y,
nil,
k,k,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.score(t)
local _,_,_,T=getColor()
setColor(1,1,1,T*.5)
draw(
t.text,t.x,t.y-0-t.c^.2*50,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
local TEXT={}
function TEXT.clear()
texts={}
end
function TEXT.show(text,x,y,font,style,spd,stop)
ins(texts,{
c=0, -- Timer
text=GC.newText(FONT.get(floor(font/5)*5 or 40),text), -- String
x=x or 0, -- X
y=y or 0, -- Y
spd=(spd or 1), -- Timing speed(1=last 1 sec)
stop=stop, -- Stop time(sustained text)
draw=assert(textFX[style or 'appear'],"no text type:"..style),-- Draw method
})
end
function TEXT.getText(text,x,y,font,style,spd,stop)-- Another version of TEXT.show(), but only return text object, need manual management
return {
c=0,
text=GC.newText(FONT.get(floor(font/5)*5 or 40),text),
x=x or 0,
y=y or 0,
spd=(spd or 1),
stop=stop,
draw=textFX[style or 'appear'] or error("unavailable type:"..style),
}
end
function TEXT.update(dt,list)
if not list then
list=texts
end
for i=#list,1,-1 do
local t=list[i]
t.c=t.c+t.spd*dt
if t.stop then
if t.c>t.stop then
t.c=t.stop
end
end
if t.c>1 then
rem(list,i)
end
end
end
function TEXT.draw(list)
if not list then
list=texts
end
for i=1,#list do
local t=list[i]
local p=t.c
setColor(1,1,1,p<.2 and p*5 or p<.8 and 1 or 5-p*5)
t:draw()
end
end
return TEXT

26
Zframework/upcaser.txt Normal file
View File

@@ -0,0 +1,26 @@
a=A,b=B,c=C,d=D,e=E,f=F,g=G,h=H,i=I,j=J,k=K,l=L,m=M,n=N,o=O,p=P,q=Q,r=R,s=S,t=T,u=U,v=V,w=W,x=X,y=Y,z=Z
µ=Μ,ß=SS,à=À,á=Á,â=Â,ã=Ã,ä=Ä,å=Å,æ=Æ,ç=Ç,è=È,é=É,ê=Ê,ë=Ë,ì=Ì,í=Í,î=Î,ï=Ï,ð=Ð,ñ=Ñ,ò=Ò,ó=Ó,ô=Ô,õ=Õ,ö=Ö,ø=Ø,ù=Ù,ú=Ú,û=Û,ü=Ü,ý=Ý,þ=Þ,ÿ=Ÿ,ā=Ā,ă=Ă,ą=Ą,ć=Ć,ĉ=Ĉ,ċ=Ċ,č=Č,ď=Ď,đ=Đ,ē=Ē,ĕ=Ĕ,ė=Ė,ę=Ę,ě=Ě,ĝ=Ĝ,ğ=Ğ,ġ=Ġ,ģ=Ģ,ĥ=Ĥ,ħ=Ħ,ĩ=Ĩ,ī=Ī,ĭ=Ĭ,į=Į,ij=IJ,ĵ=Ĵ,ķ=Ķ,ĺ=Ĺ,ļ=Ļ,ľ=Ľ,ŀ=Ŀ,ł=Ł,ń=Ń,ņ=Ņ,ň=Ň,ŋ=Ŋ,ō=Ō,ŏ=Ŏ,ő=Ő,œ=Œ,ŕ=Ŕ,ŗ=Ŗ,ř=Ř,ś=Ś,ŝ=Ŝ,ş=Ş,š=Š,ţ=Ţ,ť=Ť,ŧ=Ŧ,ũ=Ũ,ū=Ū,ŭ=Ŭ,ů=Ů,ű=Ű,ų=Ų,ŵ=Ŵ,ŷ=Ŷ,ź=Ź,ż=Ż,ž=Ž
ƀ=Ƀ,ƃ=Ƃ,ƅ=Ƅ,ƈ=Ƈ,ƌ=Ƌ,ƒ=Ƒ,ƕ=Ƕ,ƙ=Ƙ,ƚ=Ƚ,ƞ=Ƞ,ơ=Ơ,ƣ=Ƣ,ƥ=Ƥ,ƨ=Ƨ,ƭ=Ƭ,ư=Ư,ƴ=Ƴ,ƶ=Ƶ,ƹ=Ƹ,ƽ=Ƽ,ƿ=Ƿ,dž=DŽ,lj=LJ,nj=NJ,ǎ=Ǎ,ǐ=Ǐ,ǒ=Ǒ,ǔ=Ǔ,ǖ=Ǖ,ǘ=Ǘ,ǚ=Ǚ,ǜ=Ǜ,ǝ=Ǝ,ǟ=Ǟ,ǡ=Ǡ,ǣ=Ǣ,ǥ=Ǥ,ǧ=Ǧ,ǩ=Ǩ,ǫ=Ǫ,ǭ=Ǭ,ǯ=Ǯ,dz=DZ,ǵ=Ǵ,ǹ=Ǹ,ǻ=Ǻ,ǽ=Ǽ,ǿ=Ǿ,ȁ=Ȁ,ȃ=Ȃ,ȅ=Ȅ,ȇ=Ȇ,ȉ=Ȉ,ȋ=Ȋ,ȍ=Ȍ,ȏ=Ȏ,ȑ=Ȑ,ȓ=Ȓ,ȕ=Ȕ,ȗ=Ȗ,ș=Ș,ț=Ț,ȝ=Ȝ,ȟ=Ȟ,ȣ=Ȣ,ȥ=Ȥ,ȧ=Ȧ,ȩ=Ȩ,ȫ=Ȫ,ȭ=Ȭ,ȯ=Ȯ,ȱ=Ȱ,ȳ=Ȳ,ȼ=Ȼ,ȿ=Ȿ,ɀ=Ɀ,ɂ=Ɂ,ɇ=Ɇ,ɉ=Ɉ,ɋ=Ɋ,ɍ=Ɍ,ɏ=Ɏ
ɐ=Ɐ,ɑ=Ɑ,ɒ=Ɒ,ɓ=Ɓ,ɔ=Ɔ,ɖ=Ɖ,ɗ=Ɗ,ə=Ə,ɛ=Ɛ,ɜ=,ɠ=Ɠ,ɡ=Ɡ,ɣ=Ɣ,ɥ=Ɥ,ɦ=Ɦ,ɨ=Ɨ,ɩ=Ɩ,ɪ=Ɪ,ɫ=Ɫ,ɬ=Ɬ,ɯ=Ɯ,ɱ=Ɱ,ɲ=Ɲ,ɵ=Ɵ,ɽ=Ɽ,ʀ=Ʀ,ʂ=Ʂ,ʃ=Ʃ,ʇ=Ʇ,ʈ=Ʈ,ʉ=Ʉ,ʊ=Ʊ,ʋ=Ʋ,ʌ=Ʌ,ʒ=Ʒ,ʝ=,ʞ=Ʞ
ͱ=Ͱ,ͳ=Ͳ,ͷ=Ͷ,ͻ=Ͻ,ͼ=Ͼ,ͽ=Ͽ,ά=Ά,έ=Έ,ή=Ή,ί=Ί,α=Α,β=Β,γ=Γ,δ=Δ,ε=Ε,ζ=Ζ,η=Η,θ=Θ,κ=Κ,λ=Λ,ν=Ν,ξ=Ξ,ο=Ο,π=Π,ρ=Ρ,σ=Σ,τ=Τ,υ=Υ,φ=Φ,χ=Χ,ψ=Ψ,ω=Ω,ϊ=Ϊ,ϋ=Ϋ,ό=Ό,ύ=Ύ,ώ=Ώ,ϗ=Ϗ,ϙ=Ϙ,ϛ=Ϛ,ϝ=Ϝ,ϟ=Ϟ,ϡ=Ϡ,ϣ=Ϣ,ϥ=Ϥ,ϧ=Ϧ,ϩ=Ϩ,ϫ=Ϫ,ϭ=Ϭ,ϯ=Ϯ,ϲ=Ϲ,ϳ=Ϳ,ϸ=Ϸ,ϻ=Ϻ
а=А,б=Б,в=В,г=Г,д=Д,е=Е,ж=Ж,з=З,и=И,й=Й,к=К,л=Л,м=М,н=Н,о=О,п=П,р=Р,с=С,т=Т,у=У,ф=Ф,х=Х,ц=Ц,ч=Ч,ш=Ш,щ=Щ,ъ=Ъ,ы=Ы,ь=Ь,э=Э,ю=Ю,я=Я,ѐ=Ѐ,ё=Ё,ђ=Ђ,ѓ=Ѓ,є=Є,ѕ=Ѕ,і=І,ї=Ї,ј=Ј,љ=Љ,њ=Њ,ћ=Ћ,ќ=Ќ,ѝ=Ѝ,ў=Ў,џ=Џ,ѡ=Ѡ,ѣ=Ѣ,ѥ=Ѥ,ѧ=Ѧ,ѩ=Ѩ,ѫ=Ѫ,ѭ=Ѭ,ѯ=Ѯ,ѱ=Ѱ,ѳ=Ѳ,ѵ=Ѵ,ѷ=Ѷ,ѹ=Ѹ,ѻ=Ѻ,ѽ=Ѽ,ѿ=Ѿ,ҁ=Ҁ,ҋ=Ҋ,ҍ=Ҍ,ҏ=Ҏ,ґ=Ґ,ғ=Ғ,ҕ=Ҕ,җ=Җ,ҙ=Ҙ,қ=Қ,ҝ=Ҝ,ҟ=Ҟ,ҡ=Ҡ,ң=Ң,ҥ=Ҥ,ҧ=Ҧ,ҩ=Ҩ,ҫ=Ҫ,ҭ=Ҭ,ү=Ү,ұ=Ұ,ҳ=Ҳ,ҵ=Ҵ,ҷ=Ҷ,ҹ=Ҹ,һ=Һ,ҽ=Ҽ,ҿ=Ҿ,ӂ=Ӂ,ӄ=Ӄ,ӆ=Ӆ,ӈ=Ӈ,ӊ=Ӊ,ӌ=Ӌ,ӎ=Ӎ,ӏ=Ӏ,ӑ=Ӑ,ӓ=Ӓ,ӕ=Ӕ,ӗ=Ӗ,ә=Ә,ӛ=Ӛ,ӝ=Ӝ,ӟ=Ӟ,ӡ=Ӡ,ӣ=Ӣ,ӥ=Ӥ,ӧ=Ӧ,ө=Ө,ӫ=Ӫ,ӭ=Ӭ,ӯ=Ӯ,ӱ=Ӱ,ӳ=Ӳ,ӵ=Ӵ,ӷ=Ӷ,ӹ=Ӹ,ӻ=Ӻ,ӽ=Ӽ,ӿ=Ӿ,ԁ=Ԁ,ԃ=Ԃ,ԅ=Ԅ,ԇ=Ԇ,ԉ=Ԉ,ԋ=Ԋ,ԍ=Ԍ,ԏ=Ԏ,ԑ=Ԑ,ԓ=Ԓ,ԕ=Ԕ,ԗ=Ԗ,ԙ=Ԙ,ԛ=Ԛ,ԝ=Ԝ,ԟ=Ԟ,ԡ=Ԡ,ԣ=Ԣ,ԥ=Ԥ,ԧ=Ԧ,ԩ=Ԩ,ԫ=Ԫ,ԭ=Ԭ,ԯ=Ԯ
ա=Ա,բ=Բ,գ=Գ,դ=Դ,ե=Ե,զ=Զ,է=Է,ը=Ը,թ=Թ,ժ=Ժ,ի=Ի,լ=Լ,խ=Խ,ծ=Ծ,կ=Կ,հ=Հ,ձ=Ձ,ղ=Ղ,ճ=Ճ,մ=Մ,յ=Յ,ն=Ն,շ=Շ,ո=Ո,չ=Չ,պ=Պ,ջ=Ջ,ռ=Ռ,ս=Ս,վ=Վ,տ=Տ,ր=Ր,ց=Ց,ւ=Ւ,փ=Փ,ք=Ք,օ=Օ,ֆ=Ֆ
ა=Ა,ბ=Ბ,გ=Გ,დ=Დ,ე=Ე,ვ=Ვ,ზ=Ზ,თ=Თ,ი=Ი,კ=Კ,ლ=Ლ,მ=Მ,ნ=Ნ,ო=Ო,პ=Პ,ჟ=Ჟ,რ=Რ,ს=Ს,ტ=Ტ,უ=Უ,ფ=Ფ,ქ=Ქ,ღ=Ღ,=Ყ,შ=Შ,ჩ=Ჩ,ც=Ც,ძ=Ძ,წ=Წ,ჭ=Ჭ,ხ=Ხ,ჯ=Ჯ,ჰ=Ჰ,ჱ=Ჱ,ჲ=Ჲ,ჳ=Ჳ,ჴ=Ჴ,ჵ=Ჵ,ჶ=Ჶ,ჷ=Ჷ,ჸ=Ჸ,ჹ=Ჹ,ჺ=Ჺ,ჽ=Ჽ,ჾ=Ჾ,=Ჿ
ᏸ=Ᏸ,ᏹ=Ᏹ,ᏺ=Ᏺ,ᏻ=,ᏼ=,ᏽ=Ᏽ
ᲈ=Ꙋ,ᵹ=Ᵹ,ᵽ=Ᵽ,ᶎ=Ᶎ,ḁ=Ḁ,ḃ=Ḃ,ḅ=Ḅ,ḇ=Ḇ,ḉ=Ḉ,ḋ=Ḋ,ḍ=Ḍ,ḏ=Ḏ,ḑ=Ḑ,ḓ=Ḓ,ḕ=Ḕ,ḗ=Ḗ,ḙ=Ḙ,ḛ=Ḛ,ḝ=Ḝ,ḟ=Ḟ,ḡ=Ḡ,ḣ=Ḣ,ḥ=Ḥ,ḧ=Ḧ,ḩ=Ḩ,ḫ=Ḫ,ḭ=Ḭ,ḯ=Ḯ,ḱ=Ḱ,ḳ=Ḳ,ḵ=Ḵ,ḷ=Ḷ,ḹ=Ḹ,ḻ=Ḻ,ḽ=Ḽ,ḿ=Ḿ,ṁ=Ṁ,ṃ=Ṃ,ṅ=Ṅ,ṇ=Ṇ,ṉ=Ṉ,ṋ=Ṋ,ṍ=Ṍ,ṏ=Ṏ,ṑ=Ṑ,ṓ=Ṓ,ṕ=Ṕ,ṗ=Ṗ,ṙ=Ṙ,ṛ=Ṛ,ṝ=Ṝ,ṟ=Ṟ,ṡ=Ṡ,ṣ=Ṣ,ṥ=Ṥ,ṧ=Ṧ,ṩ=Ṩ,ṫ=Ṫ,ṭ=Ṭ,ṯ=Ṯ,ṱ=Ṱ,ṳ=Ṳ,ṵ=Ṵ,ṷ=Ṷ,ṹ=Ṹ,ṻ=Ṻ,ṽ=Ṽ,ṿ=Ṿ,ẁ=Ẁ,ẃ=Ẃ,ẅ=Ẅ,ẇ=Ẇ,ẉ=Ẉ,ẋ=Ẋ,ẍ=Ẍ,ẏ=Ẏ,ẑ=Ẑ,ẓ=Ẓ,ẕ=Ẕ,ạ=Ạ,ả=Ả,ấ=Ấ,ầ=Ầ,ẩ=Ẩ,ẫ=Ẫ,ậ=Ậ,ắ=Ắ,ằ=Ằ,ẳ=Ẳ,ẵ=Ẵ,ặ=Ặ,ẹ=Ẹ,ẻ=Ẻ,ẽ=Ẽ,ế=Ế,ề=Ề,ể=Ể,ễ=Ễ,ệ=Ệ,ỉ=Ỉ,ị=Ị,ọ=Ọ,ỏ=Ỏ,ố=Ố,ồ=Ồ,ổ=Ổ,ỗ=Ỗ,ộ=Ộ,ớ=Ớ,ờ=Ờ,ở=Ở,ỡ=Ỡ,ợ=Ợ,ụ=Ụ,ủ=Ủ,ứ=Ứ,ừ=Ừ,ử=Ử,ữ=Ữ,ự=Ự,ỳ=Ỳ,ỵ=Ỵ,ỷ=Ỷ,ỹ=Ỹ,ỻ=Ỻ,ỽ=Ỽ,ỿ=Ỿ,ἀ=Ἀ,ἁ=Ἁ,ἂ=Ἂ,ἃ=Ἃ,ἄ=Ἄ,ἅ=Ἅ,ἆ=Ἆ,ἇ=Ἇ,ἐ=Ἐ,ἑ=Ἑ,ἒ=Ἒ,ἓ=Ἓ,ἔ=Ἔ,ἕ=Ἕ,ἠ=Ἠ,ἡ=Ἡ,ἢ=Ἢ,ἣ=Ἣ,ἤ=Ἤ,ἥ=Ἥ,ἦ=Ἦ,ἧ=Ἧ,ἰ=Ἰ,ἱ=Ἱ,ἲ=Ἲ,ἳ=Ἳ,ἴ=Ἴ,ἵ=Ἵ,ἶ=Ἶ,ἷ=Ἷ,ὀ=Ὀ,ὁ=Ὁ,ὂ=Ὂ,ὃ=Ὃ,ὄ=Ὄ,ὅ=Ὅ,ὑ=Ὑ,ὓ=Ὓ,ὕ=Ὕ,ὗ=Ὗ,ὠ=Ὠ,ὡ=Ὡ,ὢ=Ὢ,ὣ=Ὣ,ὤ=Ὤ,ὥ=Ὥ,ὦ=Ὦ,ὧ=Ὧ,ὰ=Ὰ,ά=Ά,ὲ=Ὲ,έ=Έ,ὴ=Ὴ,ή=Ή,ὶ=Ὶ,ί=Ί,ὸ=Ὸ,ό=Ό,ὺ=Ὺ,ύ=Ύ,ὼ=Ὼ,ώ=Ώ,ᾰ=Ᾰ,ᾱ=Ᾱ,ῐ=Ῐ,ῑ=Ῑ,ῠ=Ῠ,ῡ=Ῡ,ῥ=Ῥ,ⅎ=Ⅎ
=,ⅱ=Ⅱ,ⅲ=Ⅲ,ⅳ=Ⅳ,=,ⅵ=Ⅵ,ⅶ=Ⅶ,ⅷ=Ⅷ,ⅸ=Ⅸ,=,ⅺ=Ⅺ,ⅻ=Ⅻ,=,=,=,ⅿ=,ↄ=Ↄ
ⓐ=Ⓐ,ⓑ=Ⓑ,ⓒ=Ⓒ,ⓓ=Ⓓ,ⓔ=Ⓔ,ⓕ=Ⓕ,ⓖ=Ⓖ,ⓗ=Ⓗ,ⓘ=Ⓘ,ⓙ=Ⓙ,ⓚ=Ⓚ,ⓛ=Ⓛ,ⓜ=Ⓜ,ⓝ=Ⓝ,ⓞ=Ⓞ,ⓟ=Ⓟ,ⓠ=Ⓠ,ⓡ=Ⓡ,ⓢ=Ⓢ,ⓣ=Ⓣ,ⓤ=Ⓤ,ⓥ=Ⓥ,ⓦ=Ⓦ,ⓧ=Ⓧ,ⓨ=Ⓨ,ⓩ=Ⓩ
ⰰ=Ⰰ,ⰱ=Ⰱ,ⰲ=Ⰲ,ⰳ=Ⰳ,ⰴ=Ⰴ,ⰵ=Ⰵ,ⰶ=Ⰶ,ⰷ=Ⰷ,ⰸ=Ⰸ,ⰹ=Ⰹ,ⰺ=Ⰺ,ⰻ=Ⰻ,ⰼ=Ⰼ,ⰽ=Ⰽ,ⰾ=Ⰾ,ⰿ=Ⰿ,ⱀ=Ⱀ,ⱁ=Ⱁ,ⱂ=Ⱂ,ⱃ=Ⱃ,ⱄ=Ⱄ,ⱅ=Ⱅ,ⱆ=Ⱆ,ⱇ=Ⱇ,ⱈ=Ⱈ,ⱉ=Ⱉ,ⱊ=Ⱊ,ⱋ=Ⱋ,ⱌ=Ⱌ,ⱍ=Ⱍ,ⱎ=Ⱎ,ⱏ=Ⱏ,ⱐ=Ⱐ,ⱑ=Ⱑ,ⱒ=Ⱒ,ⱓ=Ⱓ,ⱔ=Ⱔ,ⱕ=Ⱕ,ⱖ=Ⱖ,ⱗ=Ⱗ,ⱘ=Ⱘ,ⱙ=Ⱙ,ⱚ=Ⱚ,ⱛ=Ⱛ,ⱜ=Ⱜ,ⱝ=Ⱝ,ⱞ=Ⱞ,ⱟ=Ⱟ
ⱡ=Ⱡ,ⱥ=Ⱥ,ⱦ=Ⱦ,ⱨ=Ⱨ,ⱪ=Ⱪ,ⱬ=Ⱬ,ⱳ=Ⱳ,ⱶ=Ⱶ
ⲁ=Ⲁ,ⲃ=Ⲃ,=Ⲅ,ⲇ=Ⲇ,ⲉ=Ⲉ,ⲋ=Ⲋ,ⲍ=Ⲍ,ⲏ=,ⲑ=Ⲑ,ⲓ=,ⲕ=,ⲗ=Ⲗ,ⲙ=,ⲛ=,ⲝ=Ⲝ,=,ⲡ=Ⲡ,=,=,ⲧ=,ⲩ=,ⲫ=Ⲫ,ⲭ=,ⲯ=Ⲯ,ⲱ=Ⲱ,ⲳ=Ⲳ,ⲵ=Ⲵ,ⲷ=Ⲷ,ⲹ=Ⲹ,ⲻ=,ⲽ=Ⲽ,ⲿ=Ⲿ,ⳁ=Ⳁ,ⳃ=Ⳃ,ⳅ=Ⳅ,ⳇ=,ⳉ=Ⳉ,ⳋ=,ⳍ=,ⳏ=Ⳏ,ⳑ=,ⳓ=,ⳕ=Ⳕ,ⳗ=Ⳗ,ⳙ=Ⳙ,ⳛ=Ⳛ,ⳝ=Ⳝ,ⳟ=Ⳟ,ⳡ=Ⳡ,ⳣ=Ⳣ,ⳬ=Ⳬ,ⳮ=Ⳮ,ⳳ=Ⳳ
ⴀ=Ⴀ,ⴁ=Ⴁ,ⴂ=Ⴂ,ⴃ=Ⴃ,ⴄ=Ⴄ,ⴅ=Ⴅ,ⴆ=Ⴆ,ⴇ=Ⴇ,ⴈ=Ⴈ,ⴉ=Ⴉ,ⴊ=Ⴊ,ⴋ=Ⴋ,ⴌ=Ⴌ,ⴍ=Ⴍ,ⴎ=Ⴎ,ⴏ=Ⴏ,ⴐ=Ⴐ,ⴑ=Ⴑ,ⴒ=Ⴒ,ⴓ=Ⴓ,ⴔ=Ⴔ,ⴕ=Ⴕ,ⴖ=Ⴖ,ⴗ=Ⴗ,ⴘ=Ⴘ,ⴙ=Ⴙ,ⴚ=Ⴚ,ⴛ=Ⴛ,ⴜ=Ⴜ,ⴝ=Ⴝ,ⴞ=Ⴞ,ⴟ=Ⴟ,ⴠ=Ⴠ,ⴡ=Ⴡ,ⴢ=Ⴢ,ⴣ=Ⴣ,ⴤ=Ⴤ,ⴥ=Ⴥ,ⴧ=Ⴧ
ⴭ=Ⴭ,ꙁ=Ꙁ,ꙃ=Ꙃ,ꙅ=,=Ꙇ,ꙉ=Ꙉ,ꙍ=Ꙍ,ꙏ=Ꙏ,ꙑ=Ꙑ,ꙓ=Ꙓ,ꙕ=Ꙕ,ꙗ=Ꙗ,ꙙ=Ꙙ,ꙛ=Ꙛ,ꙝ=Ꙝ,ꙟ=Ꙟ,ꙡ=Ꙡ,ꙣ=Ꙣ,ꙥ=Ꙥ,ꙧ=Ꙧ,ꙩ=Ꙩ,ꙫ=Ꙫ,ꙭ=Ꙭ,ꚁ=Ꚁ,ꚃ=Ꚃ,ꚅ=Ꚅ,ꚇ=Ꚇ,ꚉ=Ꚉ,ꚋ=Ꚋ,ꚍ=Ꚍ,ꚏ=Ꚏ,ꚑ=Ꚑ,ꚓ=Ꚓ,ꚕ=Ꚕ,ꚗ=Ꚗ,ꚙ=Ꚙ,ꚛ=Ꚛ,ꜣ=Ꜣ,ꜥ=Ꜥ,ꜧ=Ꜧ,ꜩ=Ꜩ,ꜫ=Ꜫ,ꜭ=Ꜭ,ꜯ=Ꜯ,ꜳ=Ꜳ,ꜵ=Ꜵ,ꜷ=Ꜷ,ꜹ=Ꜹ,ꜻ=Ꜻ,ꜽ=Ꜽ,ꜿ=Ꜿ,ꝁ=Ꝁ,ꝃ=Ꝃ,ꝅ=Ꝅ,ꝇ=Ꝇ,ꝉ=Ꝉ,ꝋ=Ꝋ,ꝍ=Ꝍ,ꝏ=Ꝏ,ꝑ=Ꝑ,ꝓ=Ꝓ,ꝕ=Ꝕ,ꝗ=Ꝗ,ꝙ=Ꝙ,ꝛ=,ꝝ=Ꝝ,ꝟ=Ꝟ,ꝡ=Ꝡ,ꝣ=Ꝣ,ꝥ=Ꝥ,ꝧ=Ꝧ,ꝩ=Ꝩ,ꝫ=,ꝭ=Ꝭ,ꝯ=,ꝺ=Ꝺ,ꝼ=Ꝼ,ꝿ=Ꝿ,ꞁ=Ꞁ,ꞃ=Ꞃ,ꞅ=Ꞅ,ꞇ=Ꞇ,=Ꞌ,ꞑ=Ꞑ,ꞓ=Ꞓ,ꞔ=Ꞔ,ꞗ=Ꞗ,=,ꞛ=Ꞛ,ꞝ=Ꞝ,=Ꞟ,ꞡ=Ꞡ,ꞣ=Ꞣ,ꞥ=Ꞥ,ꞧ=Ꞧ,ꞩ=Ꞩ,ꞵ=,ꞷ=Ꞷ,ꞹ=Ꞹ,ꞻ=Ꞻ,ꞽ=Ꞽ,ꞿ=Ꞿ,ꟁ=Ꟁ,ꟃ=Ꟃ,ꟈ=Ꟈ,ꟊ=Ꟊ,ꟑ=Ꟑ,ꟗ=Ꟗ,ꟙ=Ꟙ,ꟶ=Ꟶ,ꭓ=
ꭰ=,ꭱ=,ꭲ=,ꭳ=Ꭳ,ꭴ=Ꭴ,=,ꭶ=Ꭶ,ꭷ=Ꭷ,ꭸ=Ꭸ,ꭹ=,ꭺ=,ꭻ=,ꭼ=,ꭽ=Ꭽ,ꭾ=,ꭿ=Ꭿ,ꮀ=Ꮀ,=Ꮁ,ꮂ=Ꮂ,=,ꮄ=Ꮄ,ꮅ=Ꮅ,ꮆ=Ꮆ,ꮇ=,ꮈ=Ꮈ,ꮉ=Ꮉ,ꮊ=Ꮊ,ꮋ=,ꮌ=Ꮌ,ꮍ=,ꮎ=Ꮎ,ꮏ=Ꮏ,ꮐ=,ꮑ=Ꮑ,ꮒ=,=,ꮔ=Ꮔ,ꮕ=Ꮕ,ꮖ=Ꮖ,ꮗ=Ꮗ,ꮘ=Ꮘ,ꮙ=Ꮙ,ꮚ=Ꮚ,ꮛ=Ꮛ,ꮜ=Ꮜ,ꮝ=Ꮝ,ꮞ=,ꮟ=,ꮠ=Ꮠ,ꮡ=Ꮡ,ꮢ=,ꮣ=Ꮣ,ꮤ=,ꮥ=,ꮦ=Ꮦ,ꮧ=Ꮧ,ꮨ=Ꮨ,=,=,ꮫ=Ꮫ,ꮬ=Ꮬ,ꮭ=Ꮭ,ꮮ=,=,ꮰ=Ꮰ,ꮱ=Ꮱ,ꮲ=,ꮳ=Ꮳ,ꮴ=Ꮴ,ꮵ=Ꮵ,ꮶ=,ꮷ=,ꮸ=Ꮸ,ꮹ=Ꮹ,ꮺ=Ꮺ,ꮻ=Ꮻ,ꮼ=Ꮼ,ꮽ=Ꮽ,ꮾ=,ꮿ=Ꮿ
=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=
𐐨=𐐀,𐐩=𐐁,𐐪=𐐂,𐐫=𐐃,𐐬=𐐄,𐐭=𐐅,𐐮=𐐆,𐐯=𐐇,𐐰=𐐈,𐐱=𐐉,𐐲=𐐊,𐐳=𐐋,𐐴=𐐌,𐐵=𐐍,𐐶=𐐎,𐐷=𐐏,𐐸=𐐐,𐐹=𐐑,𐐺=𐐒,𐐻=𐐓,𐐼=𐐔,𐐽=𐐕,𐐾=𐐖,𐐿=𐐗,𐑀=𐐘,𐑁=𐐙,𐑂=𐐚,𐑃=𐐛,𐑄=𐐜,𐑅=𐐝,𐑆=𐐞,𐑇=𐐟,𐑈=𐐠,𐑉=𐐡,𐑊=𐐢,𐑋=𐐣,𐑌=𐐤,𐑍=𐐥,𐑎=𐐦,𐑏=𐐧
𐓘=𐒰,𐓙=𐒱,𐓚=𐒲,𐓛=𐒳,𐓜=𐒴,𐓝=𐒵,𐓞=𐒶,𐓟=𐒷,𐓠=𐒸,𐓡=𐒹,𐓢=𐒺,𐓣=𐒻,𐓤=𐒼,𐓥=𐒽,𐓦=𐒾,𐓧=𐒿,𐓨=𐓀,𐓩=𐓁,𐓪=𐓂,𐓫=𐓃,𐓬=𐓄,𐓭=𐓅,𐓮=𐓆,𐓯=𐓇,𐓰=𐓈,𐓱=𐓉,𐓲=𐓊,𐓳=𐓋,𐓴=𐓌,𐓵=𐓍,𐓶=𐓎,𐓷=𐓏,𐓸=𐓐,𐓹=𐓑,𐓺=𐓒,𐓻=𐓓
𐳀=𐲀,𐳁=𐲁,𐳂=𐲂,𐳃=𐲃,𐳄=𐲄,𐳅=𐲅,𐳆=𐲆,𐳇=𐲇,𐳈=𐲈,𐳉=𐲉,𐳊=𐲊,𐳋=𐲋,𐳌=𐲌,𐳍=𐲍,𐳎=𐲎,𐳏=𐲏,𐳐=𐲐,𐳑=𐲑,𐳒=𐲒,𐳓=𐲓,𐳔=𐲔,𐳕=𐲕,𐳖=𐲖,𐳗=𐲗,𐳘=𐲘,𐳙=𐲙,𐳚=𐲚,𐳛=𐲛,𐳜=𐲜,𐳝=𐲝,𐳞=𐲞,𐳟=𐲟,𐳠=𐲠,𐳡=𐲡,𐳢=𐲢,𐳣=𐲣,𐳤=𐲤,𐳥=𐲥,𐳦=𐲦,𐳧=𐲧,𐳨=𐲨,𐳩=𐲩,𐳪=𐲪,𐳫=𐲫,𐳬=𐲬,𐳭=𐲭,𐳮=𐲮,𐳯=𐲯,𐳰=𐲰,𐳱=𐲱,𐳲=𐲲
𑣀=𑢠,𑣁=𑢡,𑣂=𑢢,𑣃=𑢣,𑣄=𑢤,𑣅=𑢥,𑣆=𑢦,𑣇=𑢧,𑣈=𑢨,𑣉=𑢩,𑣊=𑢪,𑣋=𑢫,𑣌=𑢬,𑣍=𑢭,𑣎=𑢮,𑣏=𑢯,𑣐=𑢰,𑣑=𑢱,𑣒=𑢲,𑣓=𑢳,𑣔=𑢴,𑣕=𑢵,𑣖=𑢶,𑣗=𑢷,𑣘=𑢸,𑣙=𑢹,𑣚=𑢺,𑣛=𑢻,𑣜=𑢼,𑣝=𑢽,𑣞=𑢾,𑣟=𑢿
𖹠=𖹀,𖹡=𖹁,𖹢=𖹂,𖹣=𖹃,𖹤=𖹄,𖹥=𖹅,𖹦=𖹆,𖹧=𖹇,𖹨=𖹈,𖹩=𖹉,𖹪=𖹊,𖹫=𖹋,𖹬=𖹌,𖹭=𖹍,𖹮=𖹎,𖹯=𖹏,𖹰=𖹐,𖹱=𖹑,𖹲=𖹒,𖹳=𖹓,𖹴=𖹔,𖹵=𖹕,𖹶=𖹖,𖹷=𖹗,𖹸=𖹘,𖹹=𖹙,𖹺=𖹚,𖹻=𖹛,𖹼=𖹜,𖹽=𖹝,𖹾=𖹞,𖹿=𖹟
𞤢=𞤀,𞤣=𞤁,𞤤=𞤂,𞤥=𞤃,𞤦=𞤄,𞤧=𞤅,𞤨=𞤆,𞤩=𞤇,𞤪=𞤈,𞤫=𞤉,𞤬=𞤊,𞤭=𞤋,𞤮=𞤌,𞤯=𞤍,𞤰=𞤎,𞤱=𞤏,𞤲=𞤐,𞤳=𞤑,𞤴=𞤒,𞤵=𞤓,𞤶=𞤔,𞤷=𞤕,𞤸=𞤖,𞤹=𞤗,𞤺=𞤘,𞤻=𞤙,𞤼=𞤚,𞤽=𞤛,𞤾=𞤜,𞤿=𞤝,𞥀=𞤞,𞥁=𞤟,𞥂=𞤠,𞥃=𞤡
i̇=İ,ʼn=ʼN,ǰ=J̌,ΐ=Ϊ́,ΰ=Ϋ́,և=ԵՒ,ẖ=H̱,ẗ=T̈,ẘ=W̊,ẙ=Y̊,ẚ=Aʾ,ὐ=Υ̓,ὒ=Υ̓̀,ὔ=Υ̓́,ὖ=Υ̓͂,ᾀ=ἈΙ,ᾁ=ἉΙ,ᾂ=ἊΙ,ᾃ=ἋΙ,ᾄ=ἌΙ,ᾅ=ἍΙ,ᾆ=ἎΙ,ᾇ=ἏΙ,ᾐ=ἨΙ,ᾑ=ἩΙ,ᾒ=ἪΙ,ᾓ=ἫΙ,ᾔ=ἬΙ,ᾕ=ἭΙ,ᾖ=ἮΙ,ᾗ=ἯΙ,ᾠ=ὨΙ,ᾡ=ὩΙ,ᾢ=ὪΙ,ᾣ=ὫΙ,ᾤ=ὬΙ,ᾥ=ὭΙ,ᾦ=ὮΙ,ᾧ=ὯΙ,ᾲ=ᾺΙ,ᾳ=ΑΙ,ᾴ=ΆΙ,ᾶ=Α͂,ᾷ=Α͂Ι,ῂ=ῊΙ,ῃ=ΗΙ,ῄ=ΉΙ,ῆ=Η͂,ῇ=Η͂Ι,ῒ=Ϊ̀,ῖ=Ι͂,ῗ=Ϊ͂,ῢ=Ϋ̀,ῤ=Ρ̓,ῦ=Υ͂,ῧ=Ϋ͂,ῲ=ῺΙ,ῳ=ΩΙ,ῴ=ΏΙ,ῶ=Ω͂,ῷ=Ω͂Ι,ﬓ=ՄՆ,ﬔ=ՄԵ,ﬕ=ՄԻ,ﬖ=ՎՆ,ﬗ=ՄԽ

12
Zframework/vibrate.lua Normal file
View File

@@ -0,0 +1,12 @@
local level={0,0,.01,.016,.023,.03,.04,.05,.06,.07,.08,.09,.12,.15}
local vib=love.system.vibrate
return SYSTEM=='iOS' and
function(t)
t=level[t]
if t then vib(t<=.03 and 1 or t<=.09 and 2 or 3) end
end
or
function(t)
t=level[t]
if t then vib(t) end
end

144
Zframework/voice.lua Normal file
View File

@@ -0,0 +1,144 @@
local rnd=math.random
local volume=1
local diversion=0
local voiceQueue={free=0}
local VOC={
getCount=function() return 0 end,
getQueueCount=function() return 0 end,
load=function() error("Cannot load before init!") end,
getFreeChannel=NULL,
play=NULL,
update=NULL,
}
function VOC.setDiversion(n)
assert(type(n)=='number' and n>0 and n<12,'Wrong div')
diversion=n
end
function VOC.setVol(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume')
volume=v
for i=1,#voiceQueue do
local Q=voiceQueue[i]
for j=1,#Q do
local s=Q[j]
if type(s)=='userdata' then
s:setVolume(volume)
end
end
end
end
function VOC.init(list)
VOC.init=nil
local rem=table.remove
local bank={}-- {vocName1={SRC1s},vocName2={SRC2s},...}
local Source={}
local count=#list function VOC.getCount() return count end
local function _loadVoiceFile(path,N,vocName)
local fullPath=path..vocName..'.ogg'
if love.filesystem.getInfo(fullPath) then
bank[vocName]={love.audio.newSource(fullPath,'stream')}
table.insert(Source[N],vocName)
return true
end
end
-- Load voice with string
local function _getVoice(str)
local L=bank[str]
local n=1
while L[n]:isPlaying() do
n=n+1
if not L[n] then
L[n]=L[1]:clone()
L[n]:seek(0)
break
end
end
return L[n]
end
function VOC.load(path)
for i=1,count do
Source[list[i]]={}
local n=0
repeat n=n+1 until not _loadVoiceFile(path,list[i],list[i]..'_'..n)
if n==1 then
if not _loadVoiceFile(path,list[i],list[i]) then
LOG("No VOC: "..list[i],.1)
end
end
if not Source[list[i]][1] then
Source[list[i]]=nil
end
end
function VOC.getQueueCount()
return #voiceQueue
end
function VOC.getQueueLength(chn)
return voiceQueue[chn] and #voiceQueue[chn]
end
function VOC.getFreeChannel()
local l=#voiceQueue
for i=1,l do
if #voiceQueue[i]==0 then return i end
end
voiceQueue[l+1]={s=0}
return l+1
end
function VOC.play(s,chn)
if volume>0 then
local _=Source[s]
if not _ then return end
if chn then
local L=voiceQueue[chn]
L[#L+1]=_[rnd(#_)]
L.s=1
-- Add to queue[chn]
else
voiceQueue[VOC.getFreeChannel()]={s=1,_[rnd(#_)]}
-- Create new channel & play
end
end
end
function VOC.update()
for i=#voiceQueue,1,-1 do
local Q=voiceQueue[i]
if Q.s==0 then-- Free channel, auto delete when >3
if i>3 then
rem(voiceQueue,i)
end
elseif Q.s==1 then-- Waiting load source
Q[1]=_getVoice(Q[1])
Q[1]:setVolume(volume)
Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1)))
Q[1]:play()
Q.s=Q[2] and 2 or 4
elseif Q.s==2 then-- Playing 1,ready 2
if Q[1]:getDuration()-Q[1]:tell()<.08 then
Q[2]=_getVoice(Q[2])
Q[2]:setVolume(volume)
Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1)))
Q[2]:play()
Q.s=3
end
elseif Q.s==3 then-- Playing 12 same time
if not Q[1]:isPlaying() then
for j=1,#Q do
Q[j]=Q[j+1]
end
Q.s=Q[2] and 2 or 4
end
elseif Q.s==4 then-- Playing last
if not Q[1].isPlaying(Q[1]) then
Q[1]=nil
Q.s=0
end
end
end
end
end
end
return VOC

128
Zframework/wait.lua Normal file
View File

@@ -0,0 +1,128 @@
local WAIT={
state=false,
timer=false,
totalTimer=false,
enterTime=.2,
leaveTime=.2,
timeout=6,
coverColor={.1,.1,.1},
coverAlpha=.6,
arg=false,
}
local arcAlpha={1,.6,.4,.3}
local function defaultDraw(a,t)
GC.setLineWidth(SCR.h/26)
t=t*2.6
for i=1,4 do
GC.setColor(1,1,1,a*arcAlpha[i])
GC.arc('line','open',SCR.w/2,SCR.h/2,SCR.h/5,t+MATH.tau*(i/4),t+MATH.tau*((i+1)/4))
end
end
function WAIT.new(arg)
if WAIT.state then return end
assert(type(arg)=='table',"arg must be table")
assert(arg.init==nil or type(arg.init) =='function',"Field 'enter' must be function")
assert(arg.update==nil or type(arg.update) =='function',"Field 'update' must be function")
assert(arg.quit==nil or type(arg.quit) =='function',"Field 'leave' must be function")
assert(arg.draw==nil or type(arg.draw) =='function',"Field 'draw' must be function")
assert(arg.escapable==nil or type(arg.escapable) =='boolean', "Field 'escapable' must be boolean")
if arg.init then arg.init() end
WAIT.arg=arg
WAIT.state='enter'
WAIT.timer=0
WAIT.totalTimer=0
end
function WAIT.interrupt()
if WAIT.state and WAIT.state~='leave' then
WAIT.state='leave'
WAIT.timer=WAIT.leaveTime*WAIT.timer/WAIT.enterTime
end
end
function WAIT.update(dt)
if WAIT.state then
WAIT.totalTimer=WAIT.totalTimer+dt
if WAIT.arg.update then WAIT.arg.update(dt) end
if WAIT.state~='leave' and WAIT.totalTimer>=(WAIT.arg.timeout or WAIT.timeout) then
WAIT.interrupt()
end
if WAIT.state=='enter' then
WAIT.timer=math.min(WAIT.timer+dt,WAIT.enterTime)
if WAIT.timer>=WAIT.enterTime then WAIT.state='wait' end
elseif WAIT.state=='leave' then
WAIT.timer=WAIT.timer-dt
if WAIT.timer<=0 then
WAIT.state=false
if WAIT.arg.quit then WAIT.arg.quit() end
end
end
end
end
function WAIT.draw()
if WAIT.state then
local alpha=(
WAIT.state=='enter' and WAIT.timer/WAIT.enterTime or
WAIT.state=='wait' and 1 or
WAIT.state=='leave' and WAIT.timer/WAIT.leaveTime
)
GC.setColor(
WAIT.coverColor[1],
WAIT.coverColor[2],
WAIT.coverColor[3],
alpha*WAIT.coverAlpha
)
GC.rectangle('fill',0,0,SCR.w,SCR.h);
(WAIT.arg.draw or defaultDraw)(alpha,WAIT.totalTimer)
end
end
function WAIT.setEnterTime(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.enterTime=t
end
function WAIT.setLeaveTime(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.leaveTime=t
end
function WAIT.setTimeout(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.timeout=t
end
function WAIT.setCoverColor(r,g,b)
if type(r)=='table' then
r,g,b=r[1],r[2],r[3]
end
if
type(r)=='number' and r>=0 and r<=1 and
type(g)=='number' and g>=0 and g<=1 and
type(b)=='number' and b>=0 and b<=1
then
WAIT.coverColor[1],WAIT.coverColor[2],WAIT.coverColor[3]=r,g,b
else
error("Arg must be r,g,b or {r,g,b}")
end
end
function WAIT.setCoverAlpha(a)
assert(type(a)=='number',"Arg #1 must be number between 0~1")
end
function WAIT.setDefaultDraw(f)
assert(type(f)=='function',"Arg #1 must be function")
defaultDraw=f
end
setmetatable(WAIT,{__call=function(self,arg)
self.new(arg)
end,__metatable=true})
return WAIT

194
Zframework/websocket.lua Normal file
View File

@@ -0,0 +1,194 @@
local host='127.0.0.1'
local port='80'
local path=''
-- lua + LÖVE threading websocket client
-- Original pure lua ver. by flaribbit and Particle_G
-- Threading version by MrZ
local type=type
local timer=love.timer.getTime
local TRD=love.thread.newThread("\n")
local TRD_isRunning=TRD.isRunning
local WS={}
local wsList=setmetatable({},{
__index=function(l,k)
local ws={
real=false,
status='dead',
lastPongTime=timer(),
sendTimer=0,
alertTimer=0,
pongTimer=0,
}
l[k]=ws
return ws
end
})
function WS.switchHost(_1,_2,_3)
for k in next,wsList do
WS.close(k)
end
host=_1
port=_2 or port
path=_3 or path
end
function WS.connect(name,subPath,head,timeout)
if head then
local l=""
for k,v in next,head do
l=l..(k..": "..v..'\r\n')
end
head=l
else
head=""
end
if wsList[name] and wsList[name].thread then
wsList[name].thread:release()
end
local ws={
real=true,
thread=love.thread.newThread('Zframework/websocket_thread.lua'),
triggerCHN=love.thread.newChannel(),
sendCHN=love.thread.newChannel(),
readCHN=love.thread.newChannel(),
lastPingTime=0,
lastPongTime=timer(),
pingInterval=6,
status='connecting',-- 'connecting', 'running', 'dead'
sendTimer=0,
alertTimer=0,
pongTimer=0,
}
wsList[name]=ws
ws.thread:start(ws.triggerCHN,ws.sendCHN,ws.readCHN)
ws.sendCHN:push(host)
ws.sendCHN:push(port)
ws.sendCHN:push(path..subPath)
ws.sendCHN:push(head)
ws.sendCHN:push(timeout or 2.6)
end
function WS.status(name)
local ws=wsList[name]
return ws.status or 'dead'
end
function WS.getTimers(name)
local ws=wsList[name]
return ws.pongTimer,ws.sendTimer,ws.alertTimer
end
function WS.setPingInterval(name,time)
local ws=wsList[name]
ws.pingInterval=math.max(time or 2.6,2.6)
end
function WS.alert(name)
local ws=wsList[name]
ws.alertTimer=2.6
end
local OPcode={
continue=0,
text=1,
binary=2,
close=8,
ping=9,
pong=10,
}
local OPname={
[0]='continue',
[1]='text',
[2]='binary',
[8]='close',
[9]='ping',
[10]='pong',
}
function WS.send(name,message,op)
if type(message)=='string' then
local ws=wsList[name]
if ws.real and ws.status=='running' then
ws.sendCHN:push(op and OPcode[op] or 2)-- 2=binary
ws.sendCHN:push(message)
ws.lastPingTime=timer()
ws.sendTimer=1
end
else
MES.new('error',"Attempt to send non-string value!")
MES.traceback()
end
end
function WS.read(name)
local ws=wsList[name]
if ws.real and ws.status~='connecting' and ws.readCHN:getCount()>=2 then
local op,message=ws.readCHN:pop(),ws.readCHN:pop()
if op==8 then-- 8=close
ws.status='dead'
elseif op==9 then-- 9=ping
WS.send(name,message or "",'pong')
end
ws.lastPongTime=timer()
ws.pongTimer=1
return message,OPname[op] or op
end
end
function WS.close(name)
local ws=wsList[name]
if ws.real then
ws.sendCHN:push(8)-- 8=close
ws.sendCHN:push("")
ws.status='dead'
end
end
function WS.update(dt)
local time=timer()
for name,ws in next,wsList do
if ws.real and ws.status~='dead' then
if TRD_isRunning(ws.thread) then
if ws.triggerCHN:getCount()==0 then
ws.triggerCHN:push(0)
end
if ws.status=='connecting' then
local mes=ws.readCHN:pop()
if mes then
if mes=='success' then
ws.status='running'
ws.lastPingTime=time
ws.lastPongTime=time
ws.pongTimer=1
else
ws.status='dead'
MES.new('warn',text.wsFailed:repD(mes))
end
end
elseif ws.status=='running' then
if time-ws.lastPingTime>ws.pingInterval then
WS.send(name,"",'pong')
end
if time-ws.lastPongTime>6+2*ws.pingInterval then
WS.close(name)
end
end
if ws.sendTimer>0 then ws.sendTimer=ws.sendTimer-dt end
if ws.pongTimer>0 then ws.pongTimer=ws.pongTimer-dt end
if ws.alertTimer>0 then ws.alertTimer=ws.alertTimer-dt end
else
ws.status='dead'
local err=ws.thread:getError()
if err then
MES.new('warn',text.wsClose:repD(err:match(":.-:(.-)\n")))
WS.alert(name)
end
end
end
end
end
return WS

View File

@@ -0,0 +1,189 @@
local triggerCHN,sendCHN,readCHN=...
local CHN_demand,CHN_getCount=triggerCHN.demand,triggerCHN.getCount
local CHN_push,CHN_pop=triggerCHN.push,triggerCHN.pop
local SOCK=require'socket'.tcp()
local JSON=require'Zframework.json'
do-- Connect
local host=CHN_demand(sendCHN)
local port=CHN_demand(sendCHN)
local path=CHN_demand(sendCHN)
local head=CHN_demand(sendCHN)
local timeout=CHN_demand(sendCHN)
SOCK:settimeout(timeout)
local res,err=SOCK:connect(host,port)
assert(res,err)
-- WebSocket handshake
SOCK:send(
'GET '..path..' HTTP/1.1\r\n'..
'Host: '..host..':'..port..'\r\n'..
'Connection: Upgrade\r\n'..
'Upgrade: websocket\r\n'..
'Sec-WebSocket-Version: 13\r\n'..
'Sec-WebSocket-Key: osT3F7mvlojIvf3/8uIsJQ==\r\n'..-- secKey
head..
'\r\n'
)
-- First line of HTTP
res,err=SOCK:receive('*l')
assert(res,err)
local code,ctLen
code=res:find(' ')
code=res:sub(code+1,code+3)
-- Get body length from headers and remove headers
repeat
res,err=SOCK:receive('*l')
assert(res,err)
if not ctLen and res:find('length') then
ctLen=tonumber(res:match('%d+'))
end
until res==''
-- Result
if ctLen then
if code=='101' then
CHN_push(readCHN,'success')
else
res,err=SOCK:receive(ctLen)
res=JSON.decode(assert(res,err))
error((code or "XXX")..":"..(res and res.reason or "Server Error"))
end
end
SOCK:settimeout(0)
end
local yield=coroutine.yield
local byte,char=string.byte,string.char
local band,bor,bxor=bit.band,bit.bor,bit.bxor
local shl,shr=bit.lshift,bit.rshift
local mask_key={1,14,5,14}
local mask_str=char(unpack(mask_key))
local function _send(op,message)
-- Message type
SOCK:send(char(bor(op,0x80)))
if message then
-- Length
local length=#message
if length>65535 then
SOCK:send(char(bor(127,0x80),0,0,0,0,band(shr(length,24),0xff),band(shr(length,16),0xff),band(shr(length,8),0xff),band(length,0xff)))
elseif length>125 then
SOCK:send(char(bor(126,0x80),band(shr(length,8),0xff),band(length,0xff)))
else
SOCK:send(char(bor(length,0x80)))
end
local msgbyte={byte(message,1,length)}
for i=1,length do
msgbyte[i]=bxor(msgbyte[i],mask_key[(i-1)%4+1])
end
return SOCK:send(mask_str..char(unpack(msgbyte)))
else
SOCK:send('\128'..mask_str)
return 0
end
end
local sendThread=coroutine.wrap(function()
while true do
while CHN_getCount(sendCHN)>=2 do
_send(CHN_pop(sendCHN),CHN_pop(sendCHN))
end
yield()
end
end)
local function _receive(sock,len)
local buffer=""
while true do
local r,e,p=sock:receive(len)
if r then
buffer=buffer..r
len=len-#r
elseif p then
buffer=buffer..p
len=len-#p
elseif e then
return nil,e
end
if len==0 then
return buffer
end
yield()
end
end
local readThread=coroutine.wrap(function()
local res,err
local op,fin
local lBuffer=""-- Long multi-pack buffer
while true do
-- Byte 0-1
res,err=_receive(SOCK,2)
assert(res,err)
op=band(byte(res,1),0x0f)
fin=band(byte(res,1),0x80)==0x80
-- Calculating data length
local length=band(byte(res,2),0x7f)
if length==126 then
res,err=_receive(SOCK,2)
assert(res,err)
length=shl(byte(res,1),8)+byte(res,2)
elseif length==127 then
local lenData
lenData,err=_receive(SOCK,8)
assert(res,err)
local _,_,_,_,_5,_6,_7,_8=byte(lenData,1,8)
length=shl(_5,24)+shl(_6,16)+shl(_7,8)+_8
end
res,err=_receive(SOCK,length)
assert(res,err)
-- React
if op==8 then-- 8=close
CHN_push(readCHN,8)-- close
if type(res)=='string' then
CHN_push(readCHN,res:sub(3))--[Warning] 2 bytes close code at start so :sub(3)
else
CHN_push(readCHN,"WS closed")
end
return
elseif op==0 then-- 0=continue
lBuffer=lBuffer..res
if fin then
CHN_push(readCHN,lBuffer)
lBuffer=""
end
else
CHN_push(readCHN,op)
if fin then
CHN_push(readCHN,res)
lBuffer=""
else
lBuffer=res
end
end
yield()
end
end)
local success,err
while true do-- Running
CHN_demand(triggerCHN)
success,err=pcall(sendThread)
if not success or err then break end
success,err=pcall(readThread)
if not success or err then break end
end
SOCK:close()
CHN_push(readCHN,8)-- close
CHN_push(readCHN,err or "Disconnected")
error()

View File

@@ -0,0 +1,19 @@
local love=love
local max,min=math.max,math.min
local trigDist=0
return function(y,key1,key2)
if y>0 then
trigDist=max(trigDist,0)+y^1.2
elseif y<0 then
if trigDist>0 then trigDist=0 end
trigDist=min(trigDist,0)-(-y)^1.2
end
while trigDist>=1 do
love.keypressed(key1 or 'up')
trigDist=trigDist-1
end
while trigDist<=-1 do
love.keypressed(key2 or 'down')
trigDist=trigDist+1
end
end

1525
Zframework/widget.lua Normal file

File diff suppressed because it is too large Load Diff