953 lines
30 KiB
Lua
953 lines
30 KiB
Lua
-- WARNING: This framework has been remade and renamed to Zenitha. Do not use this deprecated framework for your project
|
|
|
|
NONE={}function NULL() end PAPER=love.graphics.newCanvas(1,1)
|
|
EDITING=""
|
|
LOADED=false
|
|
|
|
---@type 'Windows'|'Android'|'Linux'|'iOS'|'macOS'|'Web'
|
|
SYSTEM=love.system.getOS()
|
|
WEB_COMPAT_MODE=false
|
|
if SYSTEM=='OS X' then
|
|
SYSTEM='macOS'
|
|
elseif SYSTEM=='Web' then
|
|
WEB_COMPAT_MODE=not love.thread.newThread('\n'):start()
|
|
print('Web compatible mode: ', WEB_COMPAT_MODE)
|
|
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)
|
|
CLIPBOARD= require'Zframework.clipboard'
|
|
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'
|
|
|
|
if SYSTEM=='Web' then
|
|
JS=require'Zframework.js'
|
|
end
|
|
|
|
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(joystick)
|
|
table.insert(jsState,{
|
|
_id=joystick:getID(),
|
|
_jsObj=joystick,
|
|
leftx=0,lefty=0,
|
|
rightx=0,righty=0,
|
|
triggerleft=0,triggerright=0
|
|
})
|
|
MES.new('info',"Joystick added")
|
|
end
|
|
function love.joystickremoved(joystick)
|
|
for i=1,#jsState do
|
|
if jsState[i]._jsObj==joystick then
|
|
for j=1,#gamePadKeys do
|
|
if joystick:isGamepadDown(gamePadKeys[j]) then
|
|
love.gamepadreleased(joystick,gamePadKeys[j])
|
|
end
|
|
end
|
|
love.gamepadaxis(joystick,'leftx',0)
|
|
love.gamepadaxis(joystick,'lefty',0)
|
|
love.gamepadaxis(joystick,'rightx',0)
|
|
love.gamepadaxis(joystick,'righty',0)
|
|
love.gamepadaxis(joystick,'triggerleft',-1)
|
|
love.gamepadaxis(joystick,'triggerright',-1)
|
|
MES.new('info',"Joystick removed")
|
|
table.remove(jsState,i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
function love.gamepadaxis(joystick,axis,val)
|
|
if jsState[1] and joystick==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(joystick,jsAxisEventName[axis][1])
|
|
elseif js[axis]~=0 then
|
|
love.gamepadreleased(joystick,jsAxisEventName[axis][2])
|
|
end
|
|
if newVal==-1 then
|
|
love.gamepadpressed(joystick,jsAxisEventName[axis][1])
|
|
elseif newVal==1 then
|
|
love.gamepadpressed(joystick,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(joystick,jsAxisEventName[axis])
|
|
else
|
|
love.gamepadreleased(joystick,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 SYSTEM == 'Web' then
|
|
JS.retrieveData(dt)
|
|
CLIPBOARD._update(dt)
|
|
end
|
|
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
|