Compare commits
43 Commits
pre0.17.1-
...
pre0.17.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb5c3c3be5 | ||
|
|
a5b9206694 | ||
|
|
375e67bdc4 | ||
|
|
724a576aa3 | ||
|
|
ed47dcb90c | ||
|
|
64b08a5a4d | ||
|
|
baed0153a2 | ||
|
|
46d95b33e4 | ||
|
|
200d270fee | ||
|
|
a8628275a0 | ||
|
|
20a1d2bcc1 | ||
|
|
b887a1f096 | ||
|
|
9bf0e9f28d | ||
|
|
dfc724767b | ||
|
|
f0e66e9dc5 | ||
|
|
0932335f0b | ||
|
|
a9b39e396a | ||
|
|
2e0ceaae72 | ||
|
|
04f38d2eb6 | ||
|
|
fc1ed4dff6 | ||
|
|
f8935d3dd7 | ||
|
|
a86228677f | ||
|
|
79df9f7876 | ||
|
|
12ea2d76be | ||
|
|
485bd72241 | ||
|
|
7240275075 | ||
|
|
29ef9b8d15 | ||
|
|
97f4795d4e | ||
|
|
226e45b24d | ||
|
|
d6ab7e72b2 | ||
|
|
168f44b8b3 | ||
|
|
b73f646a4c | ||
|
|
36cefcc000 | ||
|
|
f901c25c87 | ||
|
|
6d8478b029 | ||
|
|
9bcb040019 | ||
|
|
d977087fc0 | ||
|
|
1a330771d7 | ||
|
|
9c8c9f2106 | ||
|
|
8e075adf8f | ||
|
|
60f2a0e647 | ||
|
|
2b80f72c6b | ||
|
|
3dda0254a8 |
@@ -1,44 +1,37 @@
|
||||
local lastLoaded={}
|
||||
local maxLoadedCount=3
|
||||
local nameList={}
|
||||
local SourceObjList={}
|
||||
local volume=1
|
||||
|
||||
local BGM={
|
||||
default=false,
|
||||
getList=function()error("Cannot getList before initialize!")end,
|
||||
getCount=function()return 0 end,
|
||||
play=NULL,
|
||||
stop=NULL,
|
||||
onChange=NULL,
|
||||
--nowPlay=[str:playing ID]
|
||||
--playing=[src:playing SRC]
|
||||
--lastPlayed=[str:lastPlayed ID]
|
||||
}
|
||||
local function task_fadeOut(src)
|
||||
while true do
|
||||
coroutine.yield()
|
||||
local v=src:getVolume()-.025*volume
|
||||
src:setVolume(v>0 and v or 0)
|
||||
if v<=0 then
|
||||
src:pause()
|
||||
return true
|
||||
end
|
||||
|
||||
function BGM.getList()return nameList end
|
||||
function BGM.getCount()return #nameList end
|
||||
local function _addFile(name,path)
|
||||
if not SourceObjList[name]then
|
||||
table.insert(nameList,name)
|
||||
SourceObjList[name]={path=path,source=false}
|
||||
end
|
||||
end
|
||||
local function task_fadeIn(src)
|
||||
while true do
|
||||
coroutine.yield()
|
||||
local v=volume
|
||||
v=math.min(v,src:getVolume()+.025*v)
|
||||
src:setVolume(v)
|
||||
if v>=volume then
|
||||
return true
|
||||
function BGM.load(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
|
||||
local function check_curFadeOut(task,code,src)
|
||||
return task.code==code and task.args[1]==src
|
||||
end
|
||||
|
||||
local function _tryReleaseSources()
|
||||
local n=#lastLoaded
|
||||
while #lastLoaded>maxLoadedCount do
|
||||
@@ -75,101 +68,115 @@ function BGM.setVol(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
function BGM.init(list)
|
||||
BGM.init=nil
|
||||
|
||||
local simpList={}
|
||||
for _,v in next,list do
|
||||
table.insert(simpList,v.name)
|
||||
SourceObjList[v.name]={path=v.path,source=false}
|
||||
end
|
||||
table.sort(simpList)
|
||||
function BGM.getList()return simpList end
|
||||
local count=#simpList
|
||||
LOG(count.." BGM files added")
|
||||
function BGM.getCount()return count end
|
||||
|
||||
local function _tryLoad(name)
|
||||
if SourceObjList[name]then
|
||||
if SourceObjList[name].source then
|
||||
return true
|
||||
elseif love.filesystem.getInfo(SourceObjList[name].path)then
|
||||
SourceObjList[name].source=love.audio.newSource(SourceObjList[name].path,'stream')
|
||||
SourceObjList[name].source:setLooping(true)
|
||||
SourceObjList[name].source:setVolume(0)
|
||||
table.insert(lastLoaded,1,name)
|
||||
_tryReleaseSources()
|
||||
return true
|
||||
else
|
||||
LOG("No BGM: "..SourceObjList[name],5)
|
||||
end
|
||||
elseif name then
|
||||
LOG("No BGM: "..name,5)
|
||||
end
|
||||
end
|
||||
function BGM.play(name,args)
|
||||
name=name or BGM.default
|
||||
args=args or""
|
||||
if not _tryLoad(name)then return end
|
||||
if volume==0 then
|
||||
BGM.nowPlay=name
|
||||
BGM.playing=SourceObjList[name].source
|
||||
local function task_fadeOut(src)
|
||||
while true do
|
||||
coroutine.yield()
|
||||
local v=src:getVolume()-.025*volume
|
||||
src:setVolume(v>0 and v or 0)
|
||||
if v<=0 then
|
||||
src:pause()
|
||||
return true
|
||||
end
|
||||
if name and SourceObjList[name].source then
|
||||
if BGM.nowPlay~=name then
|
||||
if BGM.nowPlay then
|
||||
if not STRING.sArg(args,'-so')then
|
||||
TASK.new(task_fadeOut,BGM.playing)
|
||||
else
|
||||
BGM.playing:pause()
|
||||
end
|
||||
end
|
||||
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[name].source)
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
|
||||
BGM.nowPlay=name
|
||||
BGM.playing=SourceObjList[name].source
|
||||
if not STRING.sArg(args,'-si')then
|
||||
BGM.playing:setVolume(0)
|
||||
TASK.new(task_fadeIn,BGM.playing)
|
||||
else
|
||||
BGM.playing:setVolume(volume)
|
||||
BGM.playing:play()
|
||||
end
|
||||
BGM.lastPlayed=BGM.nowPlay
|
||||
BGM.playing:seek(0)
|
||||
BGM.playing:play()
|
||||
BGM.onChange(name)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
function BGM.seek(t)
|
||||
if BGM.playing then
|
||||
BGM.playing:seek(t)
|
||||
end
|
||||
end
|
||||
function BGM.continue()
|
||||
if BGM.lastPlayed then
|
||||
BGM.nowPlay,BGM.playing=BGM.lastPlayed,SourceObjList[BGM.lastPlayed].source
|
||||
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[BGM.nowPlay].source)
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
TASK.new(task_fadeIn,BGM.playing)
|
||||
BGM.playing:play()
|
||||
end
|
||||
end
|
||||
function BGM.stop(args)
|
||||
args=args or""
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
if not STRING.sArg(args,'-s')then
|
||||
if BGM.nowPlay then
|
||||
TASK.new(task_fadeOut,BGM.playing)
|
||||
end
|
||||
else
|
||||
BGM.playing:pause()
|
||||
end
|
||||
BGM.nowPlay,BGM.playing=nil
|
||||
end
|
||||
end
|
||||
local function task_fadeIn(src)
|
||||
while true do
|
||||
coroutine.yield()
|
||||
local v=volume
|
||||
v=math.min(v,src:getVolume()+.025*v)
|
||||
src:setVolume(v)
|
||||
if v>=volume then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
local function check_curFadeOut(task,code,src)
|
||||
return task.code==code and task.args[1]==src
|
||||
end
|
||||
local function _tryLoad(name)
|
||||
if SourceObjList[name]then
|
||||
if SourceObjList[name].source then
|
||||
return true
|
||||
elseif love.filesystem.getInfo(SourceObjList[name].path)then
|
||||
SourceObjList[name].source=love.audio.newSource(SourceObjList[name].path,'stream')
|
||||
SourceObjList[name].source:setVolume(0)
|
||||
table.insert(lastLoaded,1,name)
|
||||
_tryReleaseSources()
|
||||
return true
|
||||
else
|
||||
LOG("No BGM: "..SourceObjList[name],5)
|
||||
end
|
||||
elseif name then
|
||||
LOG("No BGM: "..name,5)
|
||||
end
|
||||
end
|
||||
function BGM.play(name,args)
|
||||
name=name or BGM.default
|
||||
args=args or""
|
||||
if not _tryLoad(name)or args:sArg('-preLoad')then return end
|
||||
if volume==0 then
|
||||
BGM.nowPlay=name
|
||||
BGM.playing=SourceObjList[name].source
|
||||
return true
|
||||
end
|
||||
if name and SourceObjList[name].source then
|
||||
if BGM.nowPlay~=name then
|
||||
if BGM.nowPlay then
|
||||
if not args:sArg('-sdout')then
|
||||
TASK.new(task_fadeOut,BGM.playing)
|
||||
else
|
||||
BGM.playing:pause()
|
||||
end
|
||||
end
|
||||
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[name].source)
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
|
||||
BGM.nowPlay=name
|
||||
BGM.playing=SourceObjList[name].source
|
||||
if not args:sArg('-sdin')then
|
||||
BGM.playing:setVolume(0)
|
||||
TASK.new(task_fadeIn,BGM.playing)
|
||||
else
|
||||
BGM.playing:setVolume(volume)
|
||||
BGM.playing:play()
|
||||
end
|
||||
SourceObjList[name].source:setLooping(not args:sArg('-noloop'))
|
||||
BGM.lastPlayed=BGM.nowPlay
|
||||
BGM.playing:seek(0)
|
||||
BGM.playing:play()
|
||||
BGM.onChange(name)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
function BGM.seek(t)
|
||||
if BGM.playing then
|
||||
BGM.playing:seek(t)
|
||||
end
|
||||
end
|
||||
function BGM.isPlaying()
|
||||
return BGM.playing and BGM.playing:isPlaying()
|
||||
end
|
||||
function BGM.continue()
|
||||
if BGM.lastPlayed then
|
||||
BGM.nowPlay,BGM.playing=BGM.lastPlayed,SourceObjList[BGM.lastPlayed].source
|
||||
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[BGM.nowPlay].source)
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
TASK.new(task_fadeIn,BGM.playing)
|
||||
BGM.playing:play()
|
||||
end
|
||||
end
|
||||
function BGM.stop(args)
|
||||
args=args or""
|
||||
TASK.removeTask_code(task_fadeIn)
|
||||
if not args:sArg('-s')then
|
||||
if BGM.nowPlay then
|
||||
TASK.new(task_fadeOut,BGM.playing)
|
||||
end
|
||||
elseif BGM.playing then
|
||||
BGM.playing:pause()
|
||||
end
|
||||
BGM.nowPlay,BGM.playing=nil
|
||||
end
|
||||
return BGM
|
||||
|
||||
@@ -70,15 +70,26 @@ local gc_draw,gc_line,gc_circle,gc_print=gc.draw,gc.line,gc.circle,gc.print
|
||||
|
||||
local WIDGET,SCR,SCN=WIDGET,SCR,SCN
|
||||
local xOy=SCR.xOy
|
||||
|
||||
local ITP=xOy.inverseTransformPoint
|
||||
|
||||
local max,min=math.max,math.min
|
||||
|
||||
local devMode
|
||||
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 devMode
|
||||
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 batteryImg=GC.DO{31,20,
|
||||
{'fRect',1,0,26,2},
|
||||
@@ -96,17 +107,16 @@ local function updatePowerInfo()
|
||||
gc_clear(0,0,0,.25)
|
||||
if state~='unknown'then
|
||||
gc_setLineWidth(4)
|
||||
local charging=state=='charging'
|
||||
if state=='nobattery'then
|
||||
gc_setColor(1,1,1)
|
||||
gc_setLineWidth(2)
|
||||
gc_line(74,SCR.safeX+5,100,22)
|
||||
gc_line(74,5,100,22)
|
||||
elseif pow then
|
||||
if 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)
|
||||
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
|
||||
@@ -152,7 +162,7 @@ local function _triggerMouseDown(x,y,k)
|
||||
if SCN.mouseDown then SCN.mouseDown(x,y,k)end
|
||||
WIDGET.press(x,y,k)
|
||||
lastX,lastY=x,y
|
||||
if SETTING.clickFX then SYSFX.newTap(3,x,y)end
|
||||
if showClickFX then SYSFX.newTap(3,x,y)end
|
||||
end
|
||||
local function mouse_update(dt)
|
||||
if not KBisDown('lctrl','rctrl')and KBisDown('up','down','left','right')then
|
||||
@@ -238,13 +248,13 @@ function love.touchpressed(id,x,y)
|
||||
x,y=ITP(xOy,x,y)
|
||||
lastX,lastY=x,y
|
||||
WIDGET.cursorMove(x,y)
|
||||
if SCN.touchDown then SCN.touchDown(x,y)end
|
||||
if SCN.touchDown then SCN.touchDown(x,y,id)end
|
||||
if kb.hasTextInput()then kb.setTextInput(false)end
|
||||
end
|
||||
function love.touchmoved(_,x,y,dx,dy)
|
||||
function love.touchmoved(id,x,y,dx,dy)
|
||||
if 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)end
|
||||
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)
|
||||
@@ -257,10 +267,10 @@ function love.touchreleased(id,x,y)
|
||||
WIDGET.unFocus()
|
||||
SCN.mainTouchID=false
|
||||
end
|
||||
if SCN.touchUp then SCN.touchUp(x,y)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 SETTING.clickFX then SYSFX.newTap(3,x,y)end
|
||||
if showClickFX then SYSFX.newTap(3,x,y)end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -309,7 +319,7 @@ function love.keypressed(key,_,isRep)
|
||||
MES.new('info',"DEBUG ON",.2)
|
||||
elseif key=='f11'then
|
||||
SETTING.fullscreen=not SETTING.fullscreen
|
||||
applyFullscreen()
|
||||
applySettings()
|
||||
saveSettings()
|
||||
elseif not SCN.swapping then
|
||||
if EDITING==""and(not SCN.keyDown or SCN.keyDown(key,isRep))then
|
||||
@@ -324,7 +334,7 @@ function love.keypressed(key,_,isRep)
|
||||
elseif key=='space'or key=='return'then
|
||||
mouseShow=true
|
||||
if not isRep then
|
||||
if SETTING.clickFX then SYSFX.newTap(3,mx,my)end
|
||||
if showClickFX then SYSFX.newTap(3,mx,my)end
|
||||
_triggerMouseDown(mx,my,1)
|
||||
end
|
||||
else
|
||||
@@ -452,7 +462,7 @@ function love.gamepadpressed(_,key)
|
||||
if W and W.arrowKey then W:arrowKey(key)end
|
||||
elseif key=='return'then
|
||||
mouseShow=true
|
||||
if SETTING.clickFX then SYSFX.newTap(3,mx,my)end
|
||||
if showClickFX then SYSFX.newTap(3,mx,my)end
|
||||
_triggerMouseDown(mx,my,1)
|
||||
else
|
||||
if W and W.keypress then
|
||||
@@ -481,14 +491,16 @@ function love.lowmemory()
|
||||
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()
|
||||
|
||||
SHADER.warning:send('w',w*SCR.dpi)
|
||||
onResize(w,h)
|
||||
end
|
||||
|
||||
local onFocus=NULL
|
||||
@@ -627,14 +639,6 @@ local wsImg={}do
|
||||
}
|
||||
end
|
||||
|
||||
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 function showPowerInfo()return true end
|
||||
local onQuit=NULL
|
||||
|
||||
function love.run()
|
||||
local love=love
|
||||
|
||||
@@ -649,7 +653,7 @@ function love.run()
|
||||
local FPS,MINI=love.timer.getFPS,love.window.isMinimized
|
||||
local PUMP,POLL=love.event.pump,love.event.poll
|
||||
|
||||
local timer,SETTING,VERSION=love.timer.getTime,SETTING,VERSION
|
||||
local timer,VERSION=love.timer.getTime,VERSION
|
||||
|
||||
local frameTimeList={}
|
||||
local lastFrame=timer()
|
||||
@@ -698,11 +702,10 @@ function love.run()
|
||||
|
||||
--DRAW
|
||||
if not MINI()then
|
||||
FCT=FCT+SETTING.frameMul
|
||||
FCT=FCT+frameMul
|
||||
if FCT>=100 then
|
||||
FCT=FCT-100
|
||||
|
||||
local safeX=SCR.safeX
|
||||
gc_replaceTransform(SCR.origin)
|
||||
gc_setColor(1,1,1)
|
||||
BG.draw()
|
||||
@@ -713,16 +716,14 @@ function love.run()
|
||||
TEXT_draw()
|
||||
|
||||
--Draw cursor
|
||||
if mouseShow then
|
||||
drawCursor(time,mx,my)
|
||||
end
|
||||
gc_replaceTransform(SCR.xOy_ul)
|
||||
MES_draw()
|
||||
if mouseShow then drawCursor(time,mx,my)end
|
||||
gc_replaceTransform(SCR.origin)
|
||||
MES_draw()
|
||||
|
||||
--Draw power info.
|
||||
if showPowerInfo()then
|
||||
if showPowerInfo then
|
||||
gc_setColor(1,1,1)
|
||||
gc_draw(infoCanvas,safeX,0,0,SCR.k)
|
||||
gc_draw(infoCanvas,SCR.safeX,0,0,SCR.k)
|
||||
end
|
||||
|
||||
--Draw scene swapping animation
|
||||
@@ -737,6 +738,8 @@ function love.run()
|
||||
FONT.set(20)
|
||||
mStr(VERSION.string,0,-30)
|
||||
gc_replaceTransform(SCR.xOy_dl)
|
||||
local safeX=SCR.safeX/SCR.k
|
||||
|
||||
--Draw FPS
|
||||
FONT.set(15)
|
||||
gc_setColor(1,1,1)
|
||||
@@ -794,13 +797,13 @@ function love.run()
|
||||
gc_present()
|
||||
|
||||
--SPEED UPUPUP!
|
||||
if SETTING.cleanCanvas then gc_discard()end
|
||||
if discardCanvas then gc_discard()end
|
||||
end
|
||||
end
|
||||
|
||||
--Fresh power info.
|
||||
if time-lastFreshPow>2.6 then
|
||||
if showPowerInfo()then
|
||||
if showPowerInfo then
|
||||
updatePowerInfo()
|
||||
lastFreshPow=time
|
||||
end
|
||||
@@ -818,31 +821,44 @@ function love.run()
|
||||
end
|
||||
end
|
||||
|
||||
--Keep 60fps
|
||||
_=timer()-lastFrame
|
||||
if _<.0162 then WAIT(.0162-_)end
|
||||
while timer()-lastFrame<1/60 do end
|
||||
if _<sleepInterval*.9626 then WAIT(sleepInterval*.9626-_)end
|
||||
while timer()-lastFrame<sleepInterval do end
|
||||
end
|
||||
end
|
||||
|
||||
local Z={}
|
||||
|
||||
Z.js=jsState
|
||||
Z.errData=errData
|
||||
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.setIfPowerInfo(func)showPowerInfo=func 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
|
||||
|
||||
--Change F1~F7 events of devmode (F8 mode)
|
||||
function Z.setOnFnKeys(list)
|
||||
assert(type(list)=='table')
|
||||
assert(type(list)=='table',"Z.setOnFnKeys(list): list must be a table.")
|
||||
for i=1,7 do fnKey[i]=type(list[i])=='function'and list[i]or NULL end
|
||||
end
|
||||
|
||||
function Z.setOnFocus(func)onFocus=type(func)=='function'and func or NULL end
|
||||
function Z.setOnFocus(func)onFocus=assert(type(func)=='function'and func,"Z.setOnFocus(func): func must be a function")end
|
||||
|
||||
function Z.setOnQuit(func)onQuit=type(func)=='function'and func or NULL end
|
||||
function Z.setOnResize(func)onResize=assert(type(func)=='function'and func,"Z.setOnResize(func): func must be a function")end
|
||||
|
||||
function Z.setOnQuit(func)onQuit=assert(type(func)=='function'and func,"Z.setOnQuit(func): func must be a function")end
|
||||
|
||||
return Z
|
||||
|
||||
@@ -20,4 +20,18 @@ function MATH.coin(a,b)
|
||||
end
|
||||
end
|
||||
|
||||
function MATH.interval(v,low,high)
|
||||
if v<=low then
|
||||
return low
|
||||
elseif v>=high then
|
||||
return high
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
function MATH.expApproach(a,b,k)
|
||||
return b+(a-b)*2.718281828459045^-k
|
||||
end
|
||||
|
||||
return MATH
|
||||
@@ -140,11 +140,11 @@ function profile.switch()
|
||||
switch=not switch
|
||||
if not switch then
|
||||
profile.stop()
|
||||
love.system.setClipboardText(PROFILE.report())
|
||||
PROFILE.reset()
|
||||
love.system.setClipboardText(profile.report())
|
||||
profile.reset()
|
||||
return false
|
||||
else
|
||||
PROFILE.start()
|
||||
profile.start()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local type,rem=type,table.remove
|
||||
local int,rnd=math.floor,math.random
|
||||
local interval=MATH.interval
|
||||
|
||||
local sfxList={}
|
||||
local packSetting={}
|
||||
@@ -140,7 +141,7 @@ local function _play(name,vol,pos,pitch)
|
||||
S=S[n]--AU_SRC
|
||||
if S:getChannelCount()==1 then
|
||||
if pos then
|
||||
pos=pos*stereo
|
||||
pos=interval(pos,-1,1)*stereo
|
||||
S:setPosition(pos,1-pos^2,0)
|
||||
else
|
||||
S:setPosition(0,0,0)
|
||||
|
||||
@@ -197,6 +197,9 @@ function STRING.readLine(str)
|
||||
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))
|
||||
|
||||
@@ -19,6 +19,7 @@ local sub,ins,rem=string.sub,table.insert,table.remove
|
||||
local xOy=SCR.xOy
|
||||
local FONT=FONT
|
||||
local mStr=GC.mStr
|
||||
local approach=MATH.expApproach
|
||||
|
||||
local downArrowIcon=GC.DO{40,25,{'fPoly',0,0,20,25,40,0}}
|
||||
local upArrowIcon=GC.DO{40,25,{'fPoly',0,25,20,0,40,25}}
|
||||
@@ -45,7 +46,12 @@ local function _rectangleStencil()
|
||||
gc.rectangle('fill',1,1,STW-2,STH-2)
|
||||
end
|
||||
|
||||
local onChange=NULL
|
||||
|
||||
local WIDGET={}
|
||||
|
||||
function WIDGET.setOnChange(func)onChange=assert(type(func)=='function'and func,"WIDGET.setOnChange(func): func must be a function")end
|
||||
|
||||
local widgetMetatable={
|
||||
__tostring=function(self)
|
||||
return self:getInfo()
|
||||
@@ -537,7 +543,7 @@ function slider:update(dt)
|
||||
if ATV>0 then self.ATV=max(ATV-dt*30,0)end
|
||||
end
|
||||
if not self.hide then
|
||||
self.pos=self.pos*.7+self.disp()*.3
|
||||
self.pos=approach(self.pos,self.disp(),dt*26)
|
||||
end
|
||||
end
|
||||
function slider:draw()
|
||||
@@ -1263,7 +1269,7 @@ end
|
||||
function listBox:getInfo()
|
||||
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
|
||||
end
|
||||
function WIDGET.newListBox(D)--name,x,y,w,h,lineH[,hideF][,hide][,drawF]
|
||||
function WIDGET.newListBox(D)--name,x,y,w,h,lineH,drawF[,hideF][,hide]
|
||||
local _={
|
||||
name= D.name or"_",
|
||||
|
||||
@@ -1320,16 +1326,7 @@ function WIDGET.setWidgetList(list)
|
||||
for i=1,#list do
|
||||
list[i]:reset()
|
||||
end
|
||||
if SCN.cur~='custom_field'then
|
||||
local colorList=THEME.getThemeColor()
|
||||
if not colorList then return end
|
||||
local rnd=math.random
|
||||
for _,W in next,list do
|
||||
if W.color and not W.fText then
|
||||
W.color=colorList[rnd(#colorList)]
|
||||
end
|
||||
end
|
||||
end
|
||||
onChange()
|
||||
end
|
||||
end
|
||||
function WIDGET.setScrollHeight(height)
|
||||
|
||||
35
main.lua
35
main.lua
@@ -30,7 +30,7 @@ SAVEDIR=fs.getSaveDirectory()
|
||||
|
||||
--Global Vars & Settings
|
||||
SFXPACKS={'chiptune'}
|
||||
VOCPACKS={'miya','mono','xiaoya','miku'}
|
||||
VOCPACKS={'miya',--[['mono',]]'xiaoya','miku'}
|
||||
FIRSTLAUNCH=false
|
||||
DAILYLAUNCH=false
|
||||
|
||||
@@ -62,6 +62,19 @@ BGM.setMaxSources(5)
|
||||
BGM.setChange(function(name)MES.new('music',text.nowPlaying..name,5)end)
|
||||
VOC.setDiversion(.62)
|
||||
|
||||
WIDGET.setOnChange(function()
|
||||
if SCN.cur~='custom_field'then
|
||||
local colorList=THEME.getThemeColor()
|
||||
if not colorList then return end
|
||||
local rnd=math.random
|
||||
for _,W in next,SCN.scenes[SCN.cur].widgetList do
|
||||
if W.color then
|
||||
W.color=colorList[rnd(#colorList)]
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(_LOADTIMELIST_,("Load Zframework: %.3fs"):format(TIME()-_LOADTIME_))
|
||||
|
||||
--Create shortcuts
|
||||
@@ -73,6 +86,7 @@ mDraw=GC.draw
|
||||
Snd=SFX.playSample
|
||||
string.repD=STRING.repD
|
||||
string.sArg=STRING.sArg
|
||||
string.split=STRING.split
|
||||
|
||||
--Delete all naked files (from too old version)
|
||||
FILE.clear('')
|
||||
@@ -125,9 +139,6 @@ end})
|
||||
table.insert(_LOADTIMELIST_,("Load Parts: %.3fs"):format(TIME()-_LOADTIME_))
|
||||
|
||||
--Init Zframework
|
||||
Z.setIfPowerInfo(function()
|
||||
return SETTING.powerInfo and LOADED
|
||||
end)
|
||||
do--Z.setCursor
|
||||
local normImg=GC.DO{16,16,
|
||||
{'fCirc',8,8,4},
|
||||
@@ -173,6 +184,9 @@ Z.setOnFnKeys({
|
||||
function()for k,v in next,_G do print(k,v)end end,
|
||||
function()if love['_openConsole']then love['_openConsole']()end end,
|
||||
})
|
||||
Z.setOnResize(function(w,_)
|
||||
SHADER.warning:send('w',w*SCR.dpi)
|
||||
end)
|
||||
do--Z.setOnFocus
|
||||
local function task_autoSoundOff()
|
||||
while true do
|
||||
@@ -322,11 +336,11 @@ SFX.init((function()--[Warning] Not loading files here, just get the list of sou
|
||||
end
|
||||
return L
|
||||
end)())
|
||||
BGM.init((function()
|
||||
BGM.load((function()
|
||||
local L={}
|
||||
for _,v in next,fs.getDirectoryItems('media/music')do
|
||||
if isSafeFile('media/music/'..v,"Dangerous file : %SAVE%/media/music/"..v)then
|
||||
table.insert(L,{name=v:sub(1,-5),path='media/music/'..v})
|
||||
L[v:sub(1,-5)]='media/music/'..v
|
||||
end
|
||||
end
|
||||
return L
|
||||
@@ -469,6 +483,9 @@ do
|
||||
SETTING.dascut=SETTING.dascut+1
|
||||
needSave=true
|
||||
end
|
||||
if SETTING.vocPack=='mono'then
|
||||
SETTING.vocPack='miya'
|
||||
end
|
||||
if RANKS.stack_e then
|
||||
RANKS.stack_e=nil
|
||||
RANKS.stack_h=nil
|
||||
@@ -574,7 +591,7 @@ if FIRSTLAUNCH and MOBILE then
|
||||
end
|
||||
|
||||
--Apply system setting
|
||||
applyAllSettings()
|
||||
applySettings()
|
||||
|
||||
--Load replays
|
||||
for _,fileName in next,fs.getDirectoryItems('replay')do
|
||||
@@ -652,9 +669,9 @@ if TABLE.find(arg,'--test')then
|
||||
TASK.new(function()
|
||||
while true do
|
||||
YIELD()
|
||||
if Z.errData[1]then break end
|
||||
if Z.getErr(1)then break end
|
||||
end
|
||||
LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.errData[1].mes,"\n").."\27[91m\nAborting\27[0m")
|
||||
LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.getErr(1).mes,"\n").."\27[91m\nAborting\27[0m")
|
||||
TEST.yieldN(60)
|
||||
love.event.quit(1)
|
||||
end)
|
||||
|
||||
@@ -22,12 +22,12 @@ function back.resize(w,h)
|
||||
S[i+4]=(rnd()-.5)*.01*s--Vy
|
||||
end
|
||||
end
|
||||
function back.update()
|
||||
function back.update(dt)
|
||||
local S=stars
|
||||
--Star moving
|
||||
for i=1,1260,5 do
|
||||
S[i+1]=(S[i+1]+S[i+3])%W
|
||||
S[i+2]=(S[i+2]+S[i+4])%H
|
||||
S[i+1]=(S[i+1]+S[i+3]*dt*60)%W
|
||||
S[i+2]=(S[i+2]+S[i+4]*dt*60)%H
|
||||
end
|
||||
end
|
||||
function back.draw()
|
||||
|
||||
80
parts/eventsets/master_instinct.lua
Normal file
80
parts/eventsets/master_instinct.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
local inv_lock={60,50,45,40,37, 34,32,30,28,26}
|
||||
local inv_wait={12,11,11,10,10, 10,10, 9, 9, 9}
|
||||
local inv_fall={18,16,14,13,12, 12,11,11,10,10}
|
||||
local inv_hide={20,17,14,11, 8, 5, 3, 2, 1, 0}
|
||||
local hidetimer=0
|
||||
local held=false
|
||||
|
||||
return{
|
||||
drop=0,
|
||||
lock=inv_lock[1],
|
||||
wait=inv_wait[1],
|
||||
fall=inv_fall[1],
|
||||
ghost=false,
|
||||
noTele=true,
|
||||
das=10,arr=1,
|
||||
mesDisp=function(P)
|
||||
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
|
||||
end,
|
||||
hook_drop=function(P)
|
||||
local D=P.modeData
|
||||
|
||||
local c=#P.clearedRow
|
||||
if c==0 and D.pt%100==99 then return end
|
||||
local s=c<3 and c+1 or c==3 and 5 or 7
|
||||
if P.combo>7 then s=s+2
|
||||
elseif P.combo>3 then s=s+1
|
||||
end
|
||||
D.pt=D.pt+s
|
||||
held=false
|
||||
if D.pt<1000 then
|
||||
hidetimer=0-inv_wait[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]
|
||||
if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]end
|
||||
end
|
||||
|
||||
if D.pt%100==99 then
|
||||
SFX.play('warn_1')
|
||||
elseif D.pt>=D.target then--Level up!
|
||||
s=D.target/100
|
||||
local E=P.gameEnv
|
||||
E.lock=inv_lock[s]
|
||||
E.wait=inv_wait[s]
|
||||
E.fall=inv_fall[s]
|
||||
|
||||
if s==2 then
|
||||
E.das=8
|
||||
elseif s==4 then
|
||||
BG.set('rgb')
|
||||
elseif s==5 then
|
||||
E.das=7
|
||||
elseif s==7 then
|
||||
E.das=6
|
||||
BGM.play('far')
|
||||
elseif s==10 then
|
||||
D.pt=1000
|
||||
P:win('finish')
|
||||
return
|
||||
end
|
||||
D.target=D.target+100
|
||||
P:stageComplete(s)
|
||||
SFX.play('reach')
|
||||
end
|
||||
end,
|
||||
task=function(P)
|
||||
P.modeData.pt=0
|
||||
P.modeData.target=100
|
||||
while true do
|
||||
YIELD()
|
||||
if P.holdTime==0 and P.waiting<=0 and not held then
|
||||
hidetimer=0
|
||||
held=true
|
||||
end
|
||||
hidetimer=hidetimer+1
|
||||
if hidetimer>inv_hide[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]then
|
||||
P.gameEnv.block=false
|
||||
else
|
||||
P.gameEnv.block=true
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
@@ -7,6 +7,7 @@ local gc_draw,gc_rectangle,gc_line,gc_printf=gc.draw,gc.rectangle,gc.line,gc.pri
|
||||
|
||||
local ins,rem=table.insert,table.remove
|
||||
local int,rnd=math.floor,math.random
|
||||
local approach=MATH.expApproach
|
||||
|
||||
local SETTING,GAME,SCR=SETTING,GAME,SCR
|
||||
local PLAYERS=PLAYERS
|
||||
@@ -109,23 +110,7 @@ end
|
||||
function saveSettings()
|
||||
return saveFile(SETTING,'conf/settings')
|
||||
end
|
||||
function applyLanguage()
|
||||
text=LANG.get(SETTING.locale)
|
||||
WIDGET.setLang(text.WidgetText)
|
||||
for k,v in next,TEXTOBJ do
|
||||
if rawget(text,k)then
|
||||
v:set(text[k])
|
||||
end
|
||||
end
|
||||
end
|
||||
function applyCursor()
|
||||
love.mouse.setVisible(SETTING.sysCursor)
|
||||
end
|
||||
function applyFullscreen()
|
||||
love.window.setFullscreen(SETTING.fullscreen)
|
||||
love.resize(gc.getWidth(),gc.getHeight())
|
||||
end
|
||||
do--function applyBlockSatur,applyFieldSatur(mode)
|
||||
do--function applySettings()
|
||||
local saturateValues={
|
||||
normal={0,1},
|
||||
soft={.2,.7},
|
||||
@@ -133,58 +118,79 @@ do--function applyBlockSatur,applyFieldSatur(mode)
|
||||
light={.2,.8},
|
||||
color={-.2,1.2},
|
||||
}
|
||||
function applyBlockSatur(mode)
|
||||
local m=saturateValues[mode]or saturateValues.normal
|
||||
function applySettings()
|
||||
--Apply fullscreen
|
||||
love.window.setFullscreen(SETTING.fullscreen)
|
||||
love.resize(gc.getWidth(),gc.getHeight())
|
||||
|
||||
--Apply Zframework setting
|
||||
Z.setClickFX(SETTING.clickFX)
|
||||
Z.setFrameMul(SETTING.frameMul)
|
||||
Z.setPowerInfo(SETTING.powerInfo)
|
||||
Z.setCleanCanvas(SETTING.cleanCanvas)
|
||||
|
||||
--Apply VK shape
|
||||
VK.setShape(SETTING.VKSkin)
|
||||
|
||||
--Apply sound
|
||||
love.audio.setVolume(SETTING.mainVol)
|
||||
BGM.setVol(SETTING.bgm)
|
||||
SFX.setVol(SETTING.sfx)
|
||||
VOC.setVol(SETTING.voc)
|
||||
|
||||
--Apply saturs
|
||||
local m
|
||||
m=saturateValues[SETTING.blockSatur]or saturateValues.normal
|
||||
SHADER.blockSatur:send('b',m[1])
|
||||
SHADER.blockSatur:send('k',m[2])
|
||||
end
|
||||
function applyFieldSatur(mode)
|
||||
local m=saturateValues[mode]or saturateValues.normal
|
||||
m=saturateValues[SETTING.fieldSatur]or saturateValues.normal
|
||||
SHADER.fieldSatur:send('b',m[1])
|
||||
SHADER.fieldSatur:send('k',m[2])
|
||||
end
|
||||
end
|
||||
function applyBG()
|
||||
if SETTING.bg=='on'then
|
||||
BG.unlock()
|
||||
BG.set()
|
||||
elseif SETTING.bg=='off'then
|
||||
BG.unlock()
|
||||
BG.set('gray')
|
||||
BG.send(SETTING.bgAlpha)
|
||||
BG.lock()
|
||||
elseif SETTING.bg=='custom'then
|
||||
if love.filesystem.getInfo('conf/customBG')then
|
||||
local res,image=pcall(gc.newImage,love.filesystem.newFile('conf/customBG'))
|
||||
if res then
|
||||
BG.unlock()
|
||||
BG.set('custom')
|
||||
gc.setDefaultFilter('linear','linear')
|
||||
BG.send(SETTING.bgAlpha,image)
|
||||
gc.setDefaultFilter('nearest','nearest')
|
||||
BG.lock()
|
||||
else
|
||||
MES.new('error',text.customBGloadFailed)
|
||||
|
||||
--Apply language
|
||||
text=LANG.get(SETTING.locale)
|
||||
WIDGET.setLang(text.WidgetText)
|
||||
for k,v in next,TEXTOBJ do
|
||||
if rawget(text,k)then
|
||||
v:set(text[k])
|
||||
end
|
||||
end
|
||||
|
||||
--Apply cursor
|
||||
love.mouse.setVisible(SETTING.sysCursor)
|
||||
|
||||
--Apply BG
|
||||
if SETTING.bg=='on'then
|
||||
BG.unlock()
|
||||
BG.set()
|
||||
elseif SETTING.bg=='off'then
|
||||
BG.unlock()
|
||||
BG.set('gray')
|
||||
BG.send(SETTING.bgAlpha)
|
||||
BG.lock()
|
||||
elseif SETTING.bg=='custom'then
|
||||
if love.filesystem.getInfo('conf/customBG')then
|
||||
local res,image=pcall(gc.newImage,love.filesystem.newFile('conf/customBG'))
|
||||
if res then
|
||||
BG.unlock()
|
||||
BG.set('custom')
|
||||
gc.setDefaultFilter('linear','linear')
|
||||
BG.send(SETTING.bgAlpha,image)
|
||||
gc.setDefaultFilter('nearest','nearest')
|
||||
BG.lock()
|
||||
else
|
||||
MES.new('error',text.customBGloadFailed)
|
||||
end
|
||||
else--Switch off when custom BG not found
|
||||
SETTING.bg='off'
|
||||
BG.unlock()
|
||||
BG.set('gray')
|
||||
BG.send(SETTING.bgAlpha)
|
||||
BG.lock()
|
||||
end
|
||||
else
|
||||
SETTING.bg='off'
|
||||
applyBG()
|
||||
end
|
||||
end
|
||||
end
|
||||
function applyAllSettings()
|
||||
applyFullscreen()
|
||||
love.audio.setVolume(SETTING.mainVol)
|
||||
VK.setShape(SETTING.VKSkin)
|
||||
BGM.setVol(SETTING.bgm)
|
||||
SFX.setVol(SETTING.sfx)
|
||||
VOC.setVol(SETTING.voc)
|
||||
applyBlockSatur(SETTING.blockSatur)
|
||||
applyFieldSatur(SETTING.fieldSatur)
|
||||
applyLanguage()
|
||||
applyCursor()
|
||||
applyBG()
|
||||
end
|
||||
|
||||
--Royale mode
|
||||
function randomTarget(P)--Return a random opponent for P
|
||||
@@ -792,7 +798,7 @@ do--function resetGameData(args)
|
||||
end
|
||||
do--function checkWarning()
|
||||
local max=math.max
|
||||
function checkWarning()
|
||||
function checkWarning(dt)
|
||||
local P1=PLAYERS[1]
|
||||
if P1.alive then
|
||||
if P1.frameRun%26==0 then
|
||||
@@ -812,7 +818,7 @@ do--function checkWarning()
|
||||
end
|
||||
local _=GAME.warnLVL
|
||||
if _<GAME.warnLVL0 then
|
||||
_=_*.95+GAME.warnLVL0*.05
|
||||
_=approach(_,GAME.warnLVL0,dt*6)
|
||||
elseif _>0 then
|
||||
_=max(_-.026,0)
|
||||
end
|
||||
|
||||
@@ -603,6 +603,8 @@ do--Userdata tables
|
||||
autoLogin=true,
|
||||
simpMode=false,
|
||||
sysCursor=true,
|
||||
maxFPS=60,
|
||||
frameMul=100,
|
||||
locale='zh',
|
||||
skinSet='crystal_scf',
|
||||
skin={
|
||||
@@ -625,7 +627,6 @@ do--Userdata tables
|
||||
splashFX=2,
|
||||
shakeFX=2,
|
||||
atkFX=2,
|
||||
frameMul=100,
|
||||
cleanCanvas=false,
|
||||
blockSatur='normal',
|
||||
fieldSatur='normal',
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
local HDsearch="https://harddrop.com/wiki?search="
|
||||
local HDwiki="\nVisit HD Wiki for more information"
|
||||
local HDwiki="\nVisit Hard Drop Wiki for more information."
|
||||
return{
|
||||
{"Translator Note 1",
|
||||
"",
|
||||
"help",
|
||||
"This translation of the TetroDictionary is provided by me, User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nCorrected by C₂₉H₂₅N₃O₅ (Discord: C29H25N3O5#1606).\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.",
|
||||
"This translation of the TetroDictionary is maly provided by User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.",
|
||||
"https://github.com/26F-Studio/Techmino/blob/main/parts/language/dict_en.lua",
|
||||
},
|
||||
{"Official Website",
|
||||
"official website homepage",
|
||||
"official website homepage mainpage",
|
||||
"help",
|
||||
"The official website of Techmino!\nYou can modify your profile on it",
|
||||
"http://home.techmino.org",
|
||||
},
|
||||
{"To New Players",
|
||||
"guide newbie noob",
|
||||
"guide newbie noob readme",
|
||||
"help",
|
||||
"To new players that want to get better at the game:\n\tTwo principles:\n\t1. find a version with good controls (e.g. Techmino, Tetr.io, Tetris Online, Jstris, Tetr.js). Do not use those version used for programming practice.\n\t2. Build foundations in your skills (stable Techrashes using next queue to aid decisions), don't go for fancy T-Spins from the start.\n\n\tTwo main techniques:\n\t1. familiarize yourself with spawn locations of pieces, and the controls to move the piece into each location\n\t2. Plan ahead of where to put the pieces\nHere is a article written by a well-known player in Chinese Tetris community talking about advices to new players. Click the globe to read the translated article by User670.",
|
||||
"https://github.com/user670/temp/blob/master/tips_to_those_new_to_top.md",
|
||||
@@ -39,7 +39,7 @@ return{
|
||||
{"Four.lol",
|
||||
"four wiki",
|
||||
"help",
|
||||
"An website containing collections of various openings with simple UI and very detailed consecutive PC analyses (Not recommended for new players as you may have to memorize many techniques).",
|
||||
"An website containing collections of various openings with simple UI and very detailed consecutive PC analyses (Not recommended for new players, as you may have to memorize many techniques).",
|
||||
"https://four.lol",
|
||||
},
|
||||
{"Tetris Wiki Fandom",
|
||||
@@ -59,7 +59,7 @@ return{
|
||||
{"Github Repository",
|
||||
"githubrepository sourcecode",
|
||||
"org",
|
||||
"Techmino's Github repository. Stars are appreciated.",
|
||||
"Techmino's official Github repository. Stars are appreciated.",
|
||||
"https://github.com/26F-Studio/Techmino",
|
||||
},
|
||||
{"Communities",
|
||||
@@ -69,7 +69,7 @@ return{
|
||||
"https://discord.gg/harddrop"
|
||||
},
|
||||
{"Mew",
|
||||
"mew tieba forum",
|
||||
"mew tieba forum reddit",
|
||||
"org",
|
||||
"The Mew forum owned by Chinese Tetris Research Community, and was founded in the second half of 2021. Mew is a Chinese social media that can be thought of a combination of Discord and Reddit, with many channels in a big community. Users can chat in the channels or submit posts to the channel. Mew also has a function called \"Library\" which allows storing documentations systematically. The Tetris Mew forum is currently under construction and not too much contents are available (2/Nov/2021).",
|
||||
"https://mew.fun/n/tetris",
|
||||
@@ -83,225 +83,241 @@ return{
|
||||
{"Support 1",
|
||||
"support wechat vx alipay zfb",
|
||||
"org",
|
||||
"vx/zfb-console-support",
|
||||
FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server." or "To donate to Techmino via WeChat Pay or Alipay, type \"support\" in console.",
|
||||
},
|
||||
{"Support 2",
|
||||
"support afdian",
|
||||
"org",
|
||||
"Afdian",
|
||||
FNNS and"https://www.bilibili.com/video/BV1uT4y1P7CX"or"https://afdian.net/@MrZ_26",
|
||||
FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server. The URL in this entry is a rickroll, by the way." or "To donate to Techmino via Aifadian, use the globe icon on the bottom right to open URL. Aifadian charges 6% transaction fee off your purchase.",
|
||||
FNNS and"https://youtu.be/dQw4w9WgXcQ"or"https://afdian.net/@MrZ_26",
|
||||
},
|
||||
{"Support 3",
|
||||
"support p\97\116\114\101\111\110",
|
||||
"org",
|
||||
"P\97\116\114\101\111\110",
|
||||
FNNS and"https://www.bilibili.com/video/BV1uT4y1P7CX"or"https://www.p\97\116\114\101\111\110.com/techmino",
|
||||
FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server. The URL in this entry is a rickroll, by the way." or "To donate to Techmino via P\97\116\114\101\111\110, use the globe icon on the bottom right to open URL. P\97\116\114\101\111\110 charges 7.9% + 0.30 USD transaction fee off your purchase that is greater than 3 USD.",
|
||||
FNNS and"https://youtu.be/dQw4w9WgXcQ"or"https://www.p\97\116\114\101\111\110.com/techmino",
|
||||
},
|
||||
|
||||
--Games
|
||||
{"TTT",
|
||||
"ttt tetris trainer tres bien",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player*\nTetris Trainer Tres-Bien. A hands-on tutorial of advanced techniques in modern Tetris.\nRecommended for players that can complete a 40-line Sprint with all Tetris line clears and no hold.\nCovered topics include T-Spin, finesse, SRS, and some battle setups.\nLink translated to Simplified Chinese; originally in Japanese.",
|
||||
"http://teatube.ltd/ttt",
|
||||
"Tetris Trainer Très-Bien. A hands-on tutorial of advanced techniques in modern Tetris.\nRecommended for players that can complete a 40-line Sprint with all Tetris line clears and no hold.\nCovered topics include T-Spin, finesse, SRS, and some battle setups.\nLink in Japanese.",
|
||||
"http://taninkona.web.fc2.com/ttt/",
|
||||
},
|
||||
{"TTPC",
|
||||
"ttpc tetris perfect clear challenge",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player*\nTetris Perfect Clear Challenge. The PC opener tutorial for SRS+7 Bag.\nRecommended for players that have completed TTT. You need to know SRS to play this.\nIncludes only the basic PC opener.\nLink translated to Simplified Chinese; originally in Japanese.",
|
||||
"Tetris Perfect Clear Challenge. The PC opener tutorial for SRS and 7-Bag.\nRecommended for players that have completed TTT. You need to know SRS to play this.\nIncludes only the basic PC opener.\nLink translated to Simplified Chinese; originally in Japanese.",
|
||||
"http://teatube.ltd/ttpc",
|
||||
},
|
||||
{"NAZO",
|
||||
"nazo",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player*\nAll sorts of SRS puzzles. Recommended for players that have completed TTT.\nHas T-Spin and all spin puzzles of all difficulties.\nLink translated to Simplified Chinese; originally in Japanese.",
|
||||
"All sorts of SRS puzzles. Recommended for players that have completed TTT.\nHas T-Spin and all spin puzzles of all difficulties.\nLink translated to Simplified Chinese; originally in Japanese.",
|
||||
"http://teatube.ltd/nazo",
|
||||
},
|
||||
|
||||
{"Side Note 1",
|
||||
"note nb NB DM notice",
|
||||
"game",
|
||||
"The following contents are some brief introductions about some official and fan-made Tetris games with high popularity. We make absolutely no guarantees that they would cover every Tetris game. Also, the author of this game has made some comments on some of these games. Notice that they are just personal opinions and cannot be used to judge the qualities of these games. To better differentiate between the facts and opinions, all the commentary contents are enclosed with square brackets and are separated from the main contents.",
|
||||
},
|
||||
{"King of Stackers",
|
||||
"kos kingofstackers",
|
||||
"game",
|
||||
"*Web-based | Multiplayer*\nTurn-based battle Tetris game.",
|
||||
"Browser Game | Multiplayer | Mobile Support\nKoS for short. A turn-based battle Tetris game. In this game, the players can place seven tetrominoes in his or her turn, and garbage lines can enter the field only if the player places a block that does not clear a line. This game requires careful thinking and there are multiple modes with different attack mechanics.",
|
||||
"https://kingofstackers.com/games.php",
|
||||
},
|
||||
{"Tetr.js",
|
||||
"tetrjs tetr.js",
|
||||
"game",
|
||||
"*Web-based | Single-player*\nHas newbie-friendly custom modes (most common features). Only a few on-screen control schemes are available to mobile.\nLink to Farter's Dig Mod, which itself is a mod of another version. Also has another mod called Tetr.js Enhanced.",
|
||||
"Browser Game | Singleplayer | Mobile Support\nA browser-based Tetris game. It has many professional tunings and many modes, but the visuals are simple and there are barely any animations; besides that, only a few on-screen control schemes are available to mobile.\nLink to Farter's Dig Mod, which itself is a mod of another version. Also has another mod called Tetr.js Enhanced (You can find the link on Tetris Wiki).",
|
||||
"http://farter.cn/t",
|
||||
},
|
||||
{"Tetra Legends",
|
||||
"tl tetralegends",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player*\nFeature-rich game with fancy visuals, also visualized some data that are otherwise hidden in other games, although controls aren't exactly the most comfortable. Has a rhythm mode.\nIt can be slow to load the game for the first time.",
|
||||
"Browser Game | Singleplayer | No Mobile Support\nOr TL for short. It has many single-player modes, two hidden rhythm modes, and visualizes many hidden mechanics with rich animations. The development of this game was halted for multiple reasons in December 2020.",
|
||||
"https://tetralegends.app",
|
||||
},
|
||||
{"Ascension",
|
||||
"asc ascension",
|
||||
"asc ascension ASC",
|
||||
"game",
|
||||
"Or ASC for short. A cross-platform web-based Tetris game using its own rotation system called ASC. It may take a very long time when first loading this game. It also has many single-player modes (The \"Stack\" mode in Techmino was inspired by Ascension). Battle mode is currently in the testing phase (08/20/2021).",
|
||||
"Browser Game | Singleplayer/Multiplayer\nOr ASC for short. It uses its own rotation system (also called ASC) and has many single-player modes. Battle modes are currently under beta testing (15/Dec/2021). The Stack mode in this game was also inspired by Ascension. ",
|
||||
"https://asc.winternebs.com",
|
||||
},
|
||||
{"Jstris",
|
||||
"js jstris",
|
||||
"game",
|
||||
"*Web-based | Single-player and multiplayer*\nBasic web-based battle Tetris game.",
|
||||
"Browser Game | Singleplayer/Multiplayer | Mobile Support\nOr JS for short. It has some single-player modes with multiple customizable parameters, Adjustable virtual keys layouts for mobile, but it doesn't have any animation. ",
|
||||
"https://jstris.jezevec10.com",
|
||||
},
|
||||
{"TETR.IO",
|
||||
"io tetrio tetr.io",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player and multiplayer*\nFancy online battling Tetris game.",
|
||||
"Browser Game | Singleplayer/Multiplayer\nOr IO for short. It has a ranking system and custom game with many adjustable parameters. Also, it provides desktop clients for improved performances and no ads.\n[It seems that Safari cannot open this game.]",
|
||||
"https://tetr.io",
|
||||
},
|
||||
{"Nuketris",
|
||||
"nuketris",
|
||||
"game",
|
||||
"*Web-based | Single-player and multiplayer*\nA block stacker game with 1-vs-1 ranked mode and a few single-player modes. A PC is recommended for playing this game.",
|
||||
"*Browser Game | Singleplayer/Multiplayer\nA block stacker game with 1V1 ranked matches and basic single-player modes.",
|
||||
"https://nuketris.herokuapp.com",
|
||||
},
|
||||
{"WWC",
|
||||
{"Worldwide Combos",
|
||||
"wwc worldwidecombos",
|
||||
"game",
|
||||
"*Web-based | Multiplayer*\nWorldwide Combos, a web-based worldwide 1-vs-1 battle Tetris game.",
|
||||
"Browser Game | Singleplayer/Multiplayer\nOr WWC for short. It has worldwide 1V1 ranked matches, recorded battles (which means that your opponent doesn't have to be a real person), many different rulesets, and bomb-handicapped garbage lines.",
|
||||
"https://worldwidecombos.com",
|
||||
},
|
||||
{"Tetris Friends",
|
||||
"tf tetrisfriends notrisfoes",
|
||||
"game",
|
||||
"*Web-based, no mobile support | Single-player and multiplayer*\nA now-defunct web-based Tetris game; used to be a decent battle game. An unofficial private server known as Notris Foes exists.\nBuilt using Flash, which might require workarounds to play or cannot run at all on your devices.",
|
||||
"Browser Game | Singleplayer/Multiplayer\n or TF for short, a now-defunct official Tetris game. Used to be a popular game but now nobody plays it because the website was shut down. However an unofficial private server known as \"Notris Foes\" still exists and you will need to download desktop client for full experiences.",
|
||||
},
|
||||
{"tetris.com",
|
||||
"tetris online official",
|
||||
"game",
|
||||
"The Tetris game on tetris.com. It only has one mode – marathon, and you can control the game with your mouse.",
|
||||
"Browser Game | Singleplayer\nThe Tetris game on tetris.com. It only has one mode — marathon, and you can control the game with your mouse.",
|
||||
},
|
||||
{"Tetris Gems",
|
||||
"tetris online official gem",
|
||||
"game",
|
||||
"Another Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 1 minute. There are three kinds of gem blocks with different abilities.",
|
||||
"Browser Game | Singleplayer\nAnother Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 1 minute. There are three kinds of gem blocks with different abilities.",
|
||||
},
|
||||
{"Tetris Mind Bender",
|
||||
"tetris online official gem",
|
||||
"game",
|
||||
"Another Tetris game from tetris.com. It introduced \"Mind Bender\" minoes on the basis of marathon mode. Clearing a line with a Mind Bender mino will give you either a good or bad effect.",
|
||||
"Browser Game | Singleplayer\nAnother Tetris game from tetris.com. It introduced \"Mind Bender\" minoes on the basis of marathon mode. Clearing a line with a Mind Bender mino will give you either a good or bad effect.",
|
||||
},
|
||||
|
||||
{"Techmino",
|
||||
"techmino",
|
||||
"game",
|
||||
"Cross-Platform | Singleplayer/Multiplayer\nOr Tech for short. A Tetris game developed using LÖVE. It has many single-player modes and many customizable parameters, and online multiplayer modes are gradually being developed.",
|
||||
},
|
||||
{"Falling Lightblocks",
|
||||
"fl fallinglightblocks",
|
||||
"game",
|
||||
"Browser Game/iOS/Android | Singleplayer/Multiplayer\n A cross-platform Tetris game that can be played in portrait and landscape modes. It has fixed DAS and line clear ARE. Has some customizable controls on mobile. Most of the game modes are designed based on NES classic Tetris, but there are some modern-ish modes. Battles are half turn-based, half real-time, and garbage cannot be buffered or canceled.",
|
||||
"https://golfgl.de/lightblocks/",
|
||||
},
|
||||
{"Cambridge",
|
||||
"cambridge",
|
||||
"game",
|
||||
"Cross-Platform | Singleplayer\n A Tetris game developed using LÖVE and is dedicated to creating a robust, easily customizable platform for creating new, custom game modes. Originally made by Joe Zeng, but Milla took over the development on 08/Oct/2020 starting from V0.1.5.\n — Tetris Wiki",
|
||||
},
|
||||
{"Nanamino",
|
||||
"nanamino",
|
||||
"game",
|
||||
"Windows/Android | Singleplayer\nA developing fan game which has a interesting original rotation system.",
|
||||
},
|
||||
|
||||
{"TGM",
|
||||
"tgm tetrisgrandmaster tetristhegrandmaster",
|
||||
"game",
|
||||
"*Arcade | Single-player*\nTetris The Grand Master, an arcade Tetris series that can run on Microsoft Windows. Titles like S13 or GM come from this series.\n\nTGM3 is the most well-known game in this series.",
|
||||
"Arcade | Singleplayer/Local Multiplayer\nTetris The Grand Master, an arcade Tetris series. Titles like S13 and GM come from this series.\n\nTGM3 is the most well-known game in this series.",
|
||||
},
|
||||
{"DTET",
|
||||
"dtet",
|
||||
"game",
|
||||
"*Windows | Single-player*\nA game based on TGM's Classic rule with 20G and a powerful rotation system. Decent controls, but has no customization other than control mappings. The game is a bit hard to find now and you might need to manually install required DLLs.",
|
||||
"Windows | Singleplayer\nA game based on TGM's Classic rule with 20G and a powerful rotation system. Decent controls, but has no customization other than control mappings. The game is a bit hard to find now and you may need to manually install required DLLs.",
|
||||
},
|
||||
{"Heboris",
|
||||
"hb heboris",
|
||||
"game",
|
||||
"*Windows*\nA game with Arcade-ish play style, simulates some modes of many Tetris games.",
|
||||
"Windows | Singleplayer\nA game with Arcade-ish play style, capable of simulating many modes of other Tetris games.",
|
||||
},
|
||||
{"Texmaster",
|
||||
"txm texmaster",
|
||||
"game",
|
||||
"*Windows | Single-player*\nA game with all modes from TGM which you can use to practice. Has better controls than actual TGM. The world rule is slightly different, however (eg, instant-lock soft drops, and slightly different kick tables)",
|
||||
},
|
||||
{"Cambridge",
|
||||
"cambridge",
|
||||
"game",
|
||||
"*Windows, macOS, Linux | Single-player*\nA Lua-based game engine dedicated to creating a robust, easily customizable platform for creating new, custom game modes. It was originally made by Joe Zeng, and starting with version 0.1.5 on October 8, 2020, Milla took over development of the game.\n--Tetris Wiki",
|
||||
"Windows | Singleplayer\nA game with all modes from TGM which you can use to practice. The world rule is slightly different, however (e.g. instant-lock soft drops and slightly different kick tables).",
|
||||
},
|
||||
|
||||
{"Tetris99",
|
||||
{"Tetris Effect",
|
||||
"tec tetriseffectconnected",
|
||||
"game",
|
||||
"PS/Oculus Quest/Xbox/NS/Windows | Singleplayer/Multiplayer\nOr TE(C) for short. An official Tetris game with fancy graphics and soundtracks that react to your input. The basic version (without the word \"Connected\") only has singleplayer modes. The extended version, Tetris Effect Connected, features four online battle modes, Connected (VS), Zone Battle, Score Attack, and Classic Score Attack.",
|
||||
},
|
||||
{"Tetris 99",
|
||||
"t99 tetris99",
|
||||
"game",
|
||||
"*Nintendo Switch | Multiplayer*\nA game famous for its 99-player battle royale mode and has many interesting strategies not present on traditional battle Tetris games. Also has limited single-player modes like Marathon and bot matches available as DLC.",
|
||||
"Nintendo Switch | Singleplayer/Multiplayer\nA game famous for its 99-player battle royale mode and has many interesting strategies not present on traditional battle Tetris games. Also has limited single-player modes like Marathon and bot matches available as DLC.",
|
||||
},
|
||||
{"Puyo Puyo Tetris",
|
||||
"ppt puyopuyotetris",
|
||||
"game",
|
||||
"*Multiple platforms | Single-player and multiplayer*\nA game that combines two games, Tetris and Puyo Puyo, and can battle between those two games. Has many modes for both single-player and online. The PC/Steam version has worse controls and horrible online experience, so it is not recommended.",
|
||||
"PS/NS/Xbox/Windows | Singleplayer/multiplayer\nA game that combines two games, Tetris and Puyo Puyo, and can battle between those two games. Has many modes for both single-player and online\n\n[The Steam PC version has worse controls and horrible online experience.]",
|
||||
},
|
||||
{"Tetris Online",
|
||||
"top tetrisonline",
|
||||
"game",
|
||||
"*Windows | Single-player and multiplayer*\nA now-defunct Japanese Tetris game with both online and single-player modes. Allows custom DAS/ARR but neither can be set to 0. Minor input delay. Private servers exist and is decent for new players to get started.",
|
||||
"Windows | Singleplayer/Multiplayer*\nA now-defunct Japanese Tetris game with both online and single-player modes. Allows custom DAS/ARR but neither can be set to 0. Minor input delay. Private servers do exist and is decent for new players to get started.",
|
||||
},
|
||||
{"Tetris Effect",
|
||||
"te tetriseffect",
|
||||
{"Tetra Online",
|
||||
"TO tetraonline",
|
||||
"game",
|
||||
"*PS4, Windows, Xbox | Single-player*\nFancy graphics and soundtrack that react to your actions. Not-so-good controls. You can have a go if you are into the visuals, but not exactly worth it if you are just trying to play some Tetris.",
|
||||
},
|
||||
{"Techmino",
|
||||
"techmino",
|
||||
"game",
|
||||
"*Windows, macOS, Android, Linux, iOS/iPadOS | Single-player and multiplayer*\nA game with many modes and loads of customization. Low input delay, decent controls.",
|
||||
"Windows/macOS/Linux | Singleplayer/Multiplayer\nTO for short. A Tetris game developed by Dr Ocelot and Mine. The delays are AREs are intentionally set to high values, and players who get used to Tetris games with no delays may not get used to this game.\nThe game was removed from Steam on 09/Dec/2020 due to a DMCA notice filed by TTC.\nHowever, an offline build can still be downloaded on GitHub.",
|
||||
"https://github.com/Juan-Cartes/Tetra-Offline/releases/tag/1.0",
|
||||
},
|
||||
|
||||
{"Cultris II",
|
||||
"c2 cultris2 cultrisii",
|
||||
"game",
|
||||
"*Windows, macOS, Linux | Single-player and multiplayer*\nA game focusing on speed. Has no hold and limited lockdown timer (like old school Tetris), but has customizable DAS/ARR. The main gimmick is its timer-based combos and emphasizes on speed, combo setups and digging.",
|
||||
"Windows/OS X | Singleplayer/Multiplayer\nC2 for short. Designed based on classic Tetris, Cultris II supports customizable DAS and ARR. The battle mode is focused on time-based combos, which challenges players’ speed, n-wide setups, and downstacking skills.\n[The Mac version was not being maintained for a long time. Any macOS build newer than macOS Catalina cannot run this game at all.]",
|
||||
},
|
||||
{"Nullpomino",
|
||||
"np nullpomino",
|
||||
"game",
|
||||
"*Windows | Single-player and multiplayer*\nProfessional Tetris game with extreme room for customization. You can customize almost every aspect of the game. However, this is not a beginner-friendly game (you can get lost in the menus quite easily).",
|
||||
},
|
||||
{"Touhoumino",
|
||||
"touhoumino",
|
||||
"game",
|
||||
"*Windows | Single-player*\nA Nullpomino mod with elements from Touhou Project. It is fun to play but difficult. Recommended for players with at least half-decent skills otherwise you don't even know how you die",
|
||||
},
|
||||
{"Nanamino",
|
||||
"nanamino",
|
||||
"game",
|
||||
"*Windows, Android | Single-player*\nDeveloping game, has a interesting rotation system",
|
||||
"Windows/macOS/Linux | Singleplayer/Multiplayer\nOr NP for short. A high-customizable professional Tetris game. Nearly every parameter in the game can be adjusted.\n[But the UI was outdated, and this game requires full-keyboard controls. New players may have some problems getting used to it. Also, it seems that macOS Monterey cannot run this game.]",
|
||||
},
|
||||
{"Misamino",
|
||||
"misamino",
|
||||
"game",
|
||||
"*Windows | Single-player?*\nLocal 1-vs-1 game, mainly plays turn-based mode. You can write your own bot for it (though you need to learn its API if you do).\nMisamino is also the name of its built-in bot. Said bot is also the core for the Puyo Puyo Tetris bot, Zetris.",
|
||||
"Windows | Single-player\nLocal 1V1 game, mainly plays turn-based mode. You can write your own bot for it (though you need to learn its API if you do).\nMisamino is also the name of its built-in bot.",
|
||||
},
|
||||
{"Tetris Journey",
|
||||
"huanyouji tetrisjourney mobile phone",
|
||||
{"Touhoumino",
|
||||
"touhoumino",
|
||||
"game",
|
||||
"An official mobile Tetris game developed by Tencent (available only in China). It has level modes, battle modes, and some single-player modes. You can customize the sizes and positions of the virtual keys but you cannot adjust DAS or ARR.\n The battle mode lasts for 2 minutes and if both player did not top out, the one who sent more attacks wins."
|
||||
"Windows | Singleplayer\nA fan-made Tetris game, basically Nullpomino with elements from Touhou Project. The \"Spellcards\" from Touhou was introduced in the game, and you can only get bonus scores if you can reach the target score within the given period of time.\n[Recommended for players with at least half-decent skills otherwise you don't even know how you die.]",
|
||||
},
|
||||
|
||||
{"Tetris Blitz",
|
||||
"blitz ea mobile phone",
|
||||
"game",
|
||||
"A mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing line clears continuously. There are many different power-ups available. Also, this game has no top-out mechanism. When an incoming block overlaps with existing blocks in the field, the top lines will be cleared automatically. \n\nThis game is no longer available since April 2020.",
|
||||
"iOS/Android | Singleplayer\nA mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing line clears continuously. There are many different power-ups available. Also, this game has no top-out mechanism. When an incoming block overlaps with existing blocks in the field, the top lines will be cleared automatically. \n\nThis game is no longer available since April 2020.",
|
||||
},
|
||||
{"Tetris (EA)",
|
||||
"tetris ea galaxy universe cosmos mobile phone",
|
||||
"game",
|
||||
"Another mobile Tetris game by EA. It has two control modes – Swipe and One-Touch. It also has a Galaxy Mode besides the Marathon Mode (with gravity mechanism), and the goal of this mode is to clear all Galaxy minoes before the sequence runs out.\n\nThis game is no longer available since April 2020."
|
||||
"iOS/Android | Singleplayer/Multiplayer?\nAnother mobile Tetris game by EA. It has two control modes – Swipe and One-Touch. It also has a Galaxy Mode besides the Marathon Mode (with gravity mechanism), and the goal of this mode is to clear all Galaxy minoes before the sequence runs out.\n\nThis game is no longer available since April 2020."
|
||||
},
|
||||
{"Tetris (N3TWORK)",
|
||||
"tetris n3twork mobile phone",
|
||||
"game",
|
||||
"The latest mobile Tetris from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a 100-player Royale mode. The UI is great but its controls are not so good.",
|
||||
"iOS/Android | Singleplayer\nThe mobile Tetris game from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a 100-player Royale mode.\n[The UI is great but its controls are not so good.]",
|
||||
},
|
||||
{"Tetris Beat",
|
||||
"tetris beat n3twork rhythm",
|
||||
"game",
|
||||
"A mobile Tetris game from N3TWORK. It has a \"Beat\" mode besides the Marathon mode, but in this game you only have drop the blocks in rhythm with the BGM. The effects are very heavy and the controls are not so good."
|
||||
"iOS | Singleplayer\nA mobile Tetris game from N3TWORK. It has a \"Beat\" mode besides the Marathon mode, but in this game you only have drop the blocks in rhythm with the BGM.\n[The effects are very heavy and the controls are not so good.]"
|
||||
},
|
||||
{"Tetris Journey",
|
||||
"tetrisjourney mobile phone huanyouji",
|
||||
"game",
|
||||
"iOS/Android | Singleplayer\nAn official mobile Tetris game developed by Tencent (available exclusively in China). It has level modes, battle modes, and some single-player modes. You can customize the sizes and positions of the virtual keys but you cannot adjust DAS or ARR.\n The battle mode lasts for 2 minutes and if both player did not top out, the one who sent more attacks wins."
|
||||
},
|
||||
{"JJ Tetris",
|
||||
"jjtetris",
|
||||
"game",
|
||||
"*Android | Multiplayer*\n(JJ块)\nA casual game on JJ Card Games (JJ棋牌). Portrait screen, low input delay, smooth controls. Customizable DAS/ARR and toggle-able 20G soft drop, limited control scheme customization. No hold nor B2B, no garbage buffer nor cancelling. Every attack sends at most 4 lines, combos are more powerful, otherwise similar to modern Tetris.",
|
||||
},
|
||||
{"Falling lightblock",
|
||||
"fl fallinglightblock",
|
||||
"game",
|
||||
"*Android, iOS, Web | Single-player and multiplayer*\nA game that supports many platforms. Has delays that cannot be adjusted. Can, to some extent, customize controls on mobile. Most of the modes are similar to classic Tetris, but modern-ish modes also exist. Battles are half-turn-based-half-real-time, and garbage cannot be buffered or cancelled.",
|
||||
"https://golfgl.de/lightblocks/",
|
||||
"Android | Multiplayer\n(JJ块)\nA casual game on JJ Card Games (JJ棋牌). Portrait screen, low input delay, smooth controls. Customizable DAS/ARR and toggle-able 20G soft drop, limited control scheme customization. No hold nor B2B, no garbage buffer nor cancelling. Every attack sends at most 4 lines, combos are more powerful, otherwise similar to modern Tetris.",
|
||||
},
|
||||
|
||||
{"Huopin Tetris",
|
||||
"huopin qq",
|
||||
"game",
|
||||
"*Windows | Multiplayer*\n(火拼俄罗斯)\n\nThe Tetris game on Tencent Game Center, 12-wide board, DAS/ARR the same as your typing, 1 next, no hold. Can only send garbage through Tetris (sends 3 lines) and Triple (sends 2 lines). Garbage is checker-board-shaped and is very difficult to dig through.",
|
||||
"Windows | Multiplayer\n(火拼俄罗斯)\n\nThe Tetris game on Tencent Game Center, 12-wide board, DAS/ARR the same as your typing, 1 next, no hold. Can only send garbage through Tetris (sends 3 lines) and Triple (sends 2 lines). Garbage is checker-board-shaped and is nearly impossible to dig through.",
|
||||
},
|
||||
|
||||
--Terms
|
||||
@@ -1042,9 +1058,9 @@ return{
|
||||
|
||||
--Savedata managing
|
||||
{"Console",
|
||||
"console cmd commamd minglinghang kongzhitai",
|
||||
"console cmd commamd minglinghang kongzhitai terminal",
|
||||
"command",
|
||||
"Techmino has a console that enables debugging/advanced features.\nTo access the console, repeatedly tap the Techmino logo or press the C key on the keyboard on the main menu.\n\nCareless actions in the console may result in corrupting or losing saved data. Proceed at your own risk.",
|
||||
"Techmino has a console that enables debugging/advanced features.\nTo access the console, repeatedly tap (or click) the Techmino logo or press the C key on the keyboard on the main menu.\n\nCareless actions in the console may result in corrupting or losing saved data. Proceed at your own risk.",
|
||||
},
|
||||
{"Reset setting",
|
||||
"reset setting",
|
||||
@@ -1148,7 +1164,7 @@ return{
|
||||
{"Jonas",
|
||||
"jonas",
|
||||
"name",
|
||||
"One of the top players in Classic Tetris.\nFour-times-in-a-row champion of CTWC.\n\n(1981-2021)",
|
||||
"(1981-2021) One of the top players in Classic Tetris.\nFour-times-in-a-row champion of CTWC.",
|
||||
},
|
||||
{"Joseph",
|
||||
"joseph",
|
||||
@@ -1188,10 +1204,10 @@ return{
|
||||
{"TetroDictionary",
|
||||
"zictionary tetrodictionary littlez",
|
||||
"name",
|
||||
"(or Zictionary for short) The name of this dictionary!\nIt includes brief introductions on many common terms in Tetris.\nIt used to be a chatbot in our QQ group, which was used to answer new player's FAQs. The entries in the Tetrodictionary were also inherited from the database in the chatbot.",
|
||||
"(or Zictionary for short) The name of this dictionary!\nIt includes brief introductions on many common terms in Tetris.\nIt used to be a chatbot in our QQ group, which was used to answer new player's FAQs. The entries in the Tetrodictionary were also inherited from the database in the chatbot.\nThe contents in the TetroDictionary was adapted from a variety of sources such as Tetris Wiki and Hard Drop Wiki.",
|
||||
},
|
||||
{"MrZ",
|
||||
"mrz_26",
|
||||
"mrz_26 t026 t626",
|
||||
"name",
|
||||
"Tetris Research community member, the author of Techmino.\nPersonal bests: Sprint 25.95 seconds, MPH Sprint 57 seconds, #8 on Jstris leaderboards, X rank on TETR.IO, cleared TGM3 (World rule, Shirase gold 1300).",
|
||||
"https://space.bilibili.com/225238922",
|
||||
@@ -1200,7 +1216,7 @@ return{
|
||||
{"Circu1ation",
|
||||
"circu1ation",
|
||||
"name",
|
||||
"One of the top players. First one to achieve sub-20 Sprint in China, X rank on TETR.IO.",
|
||||
"One of the top players. First one to achieve sub-20 40L Sprint in China, X rank on TETR.IO.",
|
||||
"https://space.bilibili.com/557547205",
|
||||
},
|
||||
{"Farter",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -713,6 +713,7 @@ return{
|
||||
['master_final']= {"Master", "FINAL", "20G and beyond"},
|
||||
['master_ph']= {"Master", "PHANTASM", "???"},
|
||||
['master_ex']= {"GrandMaster", "EXTRA", "An eternity shorter than an instant"},
|
||||
['master_instinct']={"Master", "INSTINCT", "What if the active piece turned invisible?"},
|
||||
['strategy_e']= {"Strategy", "EASY", "Fast 20G decision"},
|
||||
['strategy_h']= {"Strategy", "HARD", "Fast 20G decision"},
|
||||
['strategy_u']= {"Strategy", "ULTIMATE", "Fast 20G decision"},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local C=COLOR
|
||||
return{
|
||||
fallback='en',
|
||||
loadText={
|
||||
loadSFX="Memuat efek suara",
|
||||
loadSample="Memuat sampel-sampel instrumen",
|
||||
@@ -713,6 +714,7 @@ return{
|
||||
['master_final']= {"Jago", "TERAKHIR", "Lebih dari 20G"},
|
||||
['master_ph']= {"Jago", "KHAYALAN", "???"},
|
||||
['master_ex']= {"Sangat Jago", "EKSTRA", "Blok tidak kelihatan"},
|
||||
['master_instinct']={"Jago", "INSTINK", "Bagaimana jika blok terkontrol tersembunyi?"},
|
||||
['strategy_e']= {"Strategi", "MUDAH", "Keputusan 20G cepat"},
|
||||
['strategy_h']= {"Strategi", "SULIT", "Keputusan 20G cepat"},
|
||||
['strategy_u']= {"Strategi", "TERAKHIR", "Keputusan 20G cepat"},
|
||||
|
||||
@@ -712,6 +712,7 @@ return{
|
||||
['master_final']= {"大师", "终点", "究极20G:无法触及的终点"},
|
||||
['master_ph']= {"大师", "虚幻", "虚幻20G:???"},
|
||||
['master_ex']= {"宗师", "EX", "成为方块大师"},
|
||||
['master_instinct']={"大师", "本能", "隐藏当前块"},
|
||||
['strategy_e']= {"策略堆叠", "简单", "20G堆叠中速决策练习"},
|
||||
['strategy_h']= {"策略堆叠", "困难", "20G堆叠快速决策练习"},
|
||||
['strategy_u']= {"策略堆叠", "极限", "20G堆叠极速决策练习"},
|
||||
|
||||
@@ -119,6 +119,7 @@ return{
|
||||
['master_final']= {"大师", "终点", "究极20G:无法触及的终点"},
|
||||
['master_ph']= {"大师", "虚幻", "虚幻20G:???"},
|
||||
['master_ex']= {"宗师", "EX", "成为方块大师"},
|
||||
['master_instinct']={"大师", "本能", "隐藏当前块"},
|
||||
['strategy_e']= {"策略堆叠", "简单", "20G堆叠中速决策练习"},
|
||||
['strategy_h']= {"策略堆叠", "困难", "20G堆叠快速决策练习"},
|
||||
['strategy_u']= {"策略堆叠", "极限", "20G堆叠极速决策练习"},
|
||||
|
||||
@@ -709,7 +709,8 @@ return{
|
||||
['master_m']= {"主人", "M21", "20克大师赛"},
|
||||
['master_final']= {"主人", "最终", "20G及以上"},
|
||||
['master_ph']= {"主人", "幻觉", "???"},
|
||||
['master_ex']= {"大师", "额外的", "比瞬间还短的永恒"},
|
||||
['master_ex']= {"主人", "额外的", "比瞬间还短的永恒"},
|
||||
['master_instinct']={"主人", "情绪", "步行的秘密部分"},
|
||||
['strategy_e']= {"策略堆叠", "简单", "20G堆叠中速决策练习"},
|
||||
['strategy_h']= {"策略堆叠", "困难", "20G堆叠快速决策练习"},
|
||||
['strategy_u']= {"策略堆叠", "极限", "20G堆叠极速决策练习"},
|
||||
|
||||
@@ -712,6 +712,7 @@ return{
|
||||
['master_final']= {"大師", "究極", "究極20G:無法觸及的終點"},
|
||||
['master_ph']= {"大師", "虛幻", "虛幻20G:???"},
|
||||
['master_ex']= {"宗師", "EX", "成為方塊大師"},
|
||||
['master_instinct']={"大師", "本能", "隱藏當前塊"},
|
||||
['strategy_e']= {"策略堆疊", "簡單", "20G堆疊中速決策練習"},
|
||||
['strategy_h']= {"策略堆疊", "困難", "20G堆疊快速決策練習"},
|
||||
['strategy_u']= {"策略堆疊", "極限", "20G堆疊極速決策練習"},
|
||||
@@ -724,6 +725,7 @@ return{
|
||||
['blind_l']= {"隱形", "瞬隱+", "最強大腦"},
|
||||
['blind_u']= {"隱形", "啊這", "你準備好了嗎"},
|
||||
['blind_wtf']= {"隱形", "不會吧", "還沒準備好"},
|
||||
['blind_inv']= {"隱形", "倒置的", "隱形活動件"},
|
||||
['classic_e']= {"高速經典", "簡單", "高速經典"},
|
||||
['classic_h']= {"高速經典", "困難", "飛速經典"},
|
||||
['classic_u']= {"高速經典", "極限", "極速經典"},
|
||||
|
||||
24
parts/modes/master_instinct.lua
Normal file
24
parts/modes/master_instinct.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
return{
|
||||
env={
|
||||
sequence="bagES",
|
||||
freshLimit=15,
|
||||
eventSet='master_instinct',
|
||||
bg='glow',bgm='sugar fairy',
|
||||
},
|
||||
slowMark=true,
|
||||
score=function(P)return{P.modeData.pt,P.stat.time}end,
|
||||
scoreDisp=function(D)return D[1].."P "..STRING.time(D[2])end,
|
||||
comp=function(a,b)
|
||||
return a[1]>b[1]or(a[1]==b[1]and a[2]<b[2])
|
||||
end,
|
||||
getRank=function(P)
|
||||
local S=P.modeData.pt
|
||||
return
|
||||
S>=1000 and 5 or
|
||||
S>=800 and 4 or
|
||||
S>=500 and 3 or
|
||||
S>=300 and 2 or
|
||||
S>=100 and 1 or
|
||||
S>=60 and 0
|
||||
end,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
return{
|
||||
env={
|
||||
drop=1e99,lock=1e99,
|
||||
drop=120,lock=1e99,
|
||||
infHold=true,
|
||||
ospin=false,
|
||||
eventSet='tsd_e',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
return{
|
||||
env={
|
||||
drop=30,lock=60,
|
||||
drop=60,lock=60,
|
||||
freshLimit=15,
|
||||
ospin=false,
|
||||
eventSet='tsd_h',
|
||||
|
||||
@@ -79,7 +79,7 @@ end
|
||||
--Parse notice
|
||||
local function _parseNotice(str)
|
||||
if str:find("///")then
|
||||
str=STRING.split(str,"///")
|
||||
str=str:split("///")
|
||||
for i=1,#str do
|
||||
local m=str[i]
|
||||
if m:find("=")then
|
||||
@@ -279,7 +279,7 @@ function NET.loadSavedData(sections)
|
||||
|
||||
TABLE.cover(NET.cloudData.SETTING,SETTING)
|
||||
success=success and saveSettings()
|
||||
applyAllSettings()
|
||||
applySettings()
|
||||
|
||||
TABLE.cover(NET.cloudData.keyMap,KEY_MAP)
|
||||
success=success and saveFile(KEY_MAP,'conf/key')
|
||||
|
||||
@@ -7,6 +7,7 @@ local rnd,min=math.random,math.min
|
||||
local sin,cos=math.sin,math.cos
|
||||
local ins,rem=table.insert,table.remove
|
||||
local setFont=FONT.set
|
||||
local approach=MATH.expApproach
|
||||
|
||||
local posLists={
|
||||
--1~5
|
||||
@@ -168,7 +169,7 @@ function NETPLY.setConnect(uid)PLYmap[uid].connected=true end
|
||||
function NETPLY.setPlace(uid,place)PLYmap[uid].place=place end
|
||||
function NETPLY.setStat(uid,S)
|
||||
PLYmap[uid].stat={
|
||||
lpm=("%.1f %s"):format(S.row/S.time*60,text.radarData[5]),
|
||||
lpm=("%.1f %s"):format(S.piece/S.time*24,text.radarData[5]),
|
||||
apm=("%.1f %s"):format(S.atk/S.time*60,text.radarData[3]),
|
||||
adpm=("%.1f %s"):format((S.atk+S.dig)/S.time*60,text.radarData[2]),
|
||||
}
|
||||
@@ -193,14 +194,15 @@ function NETPLY.mouseMove(x,y)
|
||||
end
|
||||
end
|
||||
|
||||
function NETPLY.update()
|
||||
function NETPLY.update(dt)
|
||||
for i=1,#PLYlist do
|
||||
local p=PLYlist[i]
|
||||
local t=posList[i]
|
||||
p.x=p.x*.9+t.x*.1
|
||||
p.y=p.y*.9+t.y*.1
|
||||
p.w=p.w*.9+t.w*.1
|
||||
p.h=p.h*.9+t.h*.1
|
||||
local d=dt*12
|
||||
p.x=approach(p.x,t.x,d)
|
||||
p.y=approach(p.y,t.y,d)
|
||||
p.w=approach(p.w,t.w,d)
|
||||
p.h=approach(p.h,t.h,d)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -904,10 +904,10 @@ function draw.norm(P,repMode)
|
||||
setFont(25)
|
||||
local tm=STRING.time(P.stat.time)
|
||||
gc_setColor(0,0,0,.3)
|
||||
gc_print(P.score1,18,509)
|
||||
gc_print(ceil(P.score1),18,509)
|
||||
gc_print(tm,18,539)
|
||||
gc_setColor(.97,.97,.92)
|
||||
gc_print(P.score1,20,510)
|
||||
gc_print(ceil(P.score1),20,510)
|
||||
gc_setColor(.85,.9,.97)
|
||||
gc_print(tm,20,540)
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ local function _newEmptyPlayer(id,mini)
|
||||
P.seqRND=love.math.newRandomGenerator(GAME.seed)
|
||||
P.atkRND=love.math.newRandomGenerator(GAME.seed)
|
||||
P.holeRND=love.math.newRandomGenerator(GAME.seed)
|
||||
P.aiRND=love.math.newRandomGenerator(GAME.seed)
|
||||
P.aiRND=love.math.newRandomGenerator(GAME.seed+P.id)
|
||||
|
||||
--Field-related
|
||||
P.field,P.visTime={},{}
|
||||
|
||||
@@ -8,6 +8,7 @@ local int,ceil,rnd=math.floor,math.ceil,math.random
|
||||
local max,min,abs,modf=math.max,math.min,math.abs,math.modf
|
||||
local assert,ins,rem=assert,table.insert,table.remove
|
||||
local resume,yield,status=coroutine.resume,coroutine.yield,coroutine.status
|
||||
local approach=MATH.expApproach
|
||||
|
||||
local SFX,BGM,VOC,VIB,SYSFX=SFX,BGM,VOC,VIB,SYSFX
|
||||
local FREEROW,TABLE,TEXT,TASK=LINE,TABLE,TEXT,TASK
|
||||
@@ -1823,7 +1824,7 @@ do
|
||||
end
|
||||
piece.pc=true
|
||||
piece.special=true
|
||||
elseif cc>1 or #self.field==self.garbageBeneath then
|
||||
elseif cc>1 or self.field[#self.field].garbage then
|
||||
self:showText(text.HPC,0,-80,50,'fly')
|
||||
atk=atk+4
|
||||
exblock=exblock+2
|
||||
@@ -2166,7 +2167,7 @@ end
|
||||
--------------------------</Tick>--------------------------
|
||||
|
||||
--------------------------<Event>--------------------------
|
||||
local function _updateMisc(P)
|
||||
local function _updateMisc(P,dt)
|
||||
--Finesse combo animation
|
||||
if P.finesseComboTime>0 then
|
||||
P.finesseComboTime=P.finesseComboTime-1
|
||||
@@ -2216,7 +2217,7 @@ local function _updateMisc(P)
|
||||
end
|
||||
local f=P.fieldUp
|
||||
if f~=y then
|
||||
P.fieldUp=f>y and max(f*.95+y*.05-2,y)or min(f*.97+y*.03+1,y)
|
||||
P.fieldUp=f>y and max(approach(f,y,dt*6)-2,y)or min(approach(f,y,dt*3)+1,y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2225,7 +2226,7 @@ local function _updateMisc(P)
|
||||
if P.stat.score-P.score1<10 then
|
||||
P.score1=P.score1+1
|
||||
else
|
||||
P.score1=int(min(P.score1*.9+P.stat.score*.1+1))
|
||||
P.score1=approach(P.score1,P.stat.score,dt*62)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2302,7 +2303,7 @@ local function _updateFX(P,dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
local function update_alive(P)
|
||||
local function update_alive(P,dt)
|
||||
local ENV=P.gameEnv
|
||||
|
||||
P.frameRun=P.frameRun+1
|
||||
@@ -2332,11 +2333,11 @@ local function update_alive(P)
|
||||
|
||||
if P.timing then P.stat.frame=P.stat.frame+1 end
|
||||
|
||||
--Calculate key speed
|
||||
--Calculate drop speed
|
||||
do
|
||||
local v=0
|
||||
for i=2,10 do v=v+i*(i-1)*72/(P.frameRun-P.dropTime[i])end
|
||||
P.dropSpeed=P.dropSpeed*.99+v*.01
|
||||
P.dropSpeed=approach(P.dropSpeed,v,dt)
|
||||
end
|
||||
|
||||
if P.gameEnv.layout=='royale'then
|
||||
@@ -2532,14 +2533,15 @@ local function update_alive(P)
|
||||
::THROW_stop::
|
||||
|
||||
--B2B bar animation
|
||||
if P.b2b1==P.b2b then
|
||||
elseif P.b2b1<P.b2b then
|
||||
P.b2b1=min(P.b2b1*.98+P.b2b*.02+.4,P.b2b)
|
||||
else
|
||||
P.b2b1=max(P.b2b1*.95+P.b2b*.05-.6,P.b2b)
|
||||
if P.b2b1~=P.b2b then
|
||||
if P.b2b1<P.b2b then
|
||||
P.b2b1=min(approach(P.b2b1,P.b2b,dt*6)+.4,P.b2b)
|
||||
else
|
||||
P.b2b1=max(approach(P.b2b1,P.b2b,dt*12)-.6,P.b2b)
|
||||
end
|
||||
end
|
||||
|
||||
_updateMisc(P)
|
||||
_updateMisc(P,dt)
|
||||
--[[
|
||||
P:setPosition(
|
||||
640-150-(30*(P.curX+P.cur.sc[2])-15),
|
||||
@@ -2590,11 +2592,11 @@ local function update_streaming(P)
|
||||
eventTime=P.stream[P.streamProgress]
|
||||
end
|
||||
end
|
||||
local function update_dead(P)
|
||||
local function update_dead(P,dt)
|
||||
local S=P.stat
|
||||
|
||||
--Final average speed
|
||||
P.dropSpeed=P.dropSpeed*.96+S.piece/S.frame*144
|
||||
P.dropSpeed=approach(P.dropSpeed,S.piece/S.frame*3600,dt)
|
||||
|
||||
if P.gameEnv.layout=='royale'then
|
||||
P.swappingAtkMode=min(P.swappingAtkMode+2,30)
|
||||
@@ -2606,7 +2608,7 @@ local function update_dead(P)
|
||||
if P.b2b1>0 then
|
||||
P.b2b1=max(0,P.b2b1*.92-1)
|
||||
end
|
||||
_updateMisc(P)
|
||||
_updateMisc(P,dt)
|
||||
end
|
||||
function Player:_die()
|
||||
self.alive=false
|
||||
@@ -2664,17 +2666,17 @@ function Player:update(dt)
|
||||
20
|
||||
do
|
||||
update_streaming(self)
|
||||
update_alive(self)
|
||||
update_alive(self,dt)
|
||||
end
|
||||
end
|
||||
else
|
||||
update_alive(self)
|
||||
update_alive(self,dt)
|
||||
end
|
||||
self.trigFrame=self.trigFrame-1
|
||||
end
|
||||
else
|
||||
while self.trigFrame>=1 do
|
||||
update_dead(self)
|
||||
update_dead(self,dt)
|
||||
self.trigFrame=self.trigFrame-1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -232,7 +232,7 @@ local commands={}do
|
||||
commands.mv={
|
||||
code=function(arg)
|
||||
--Check arguments
|
||||
arg=STRING.split(arg," ")
|
||||
arg=arg:split(" ")
|
||||
if #arg>2 then
|
||||
log{C.lY,"Warning: file names must have no spaces"}
|
||||
return
|
||||
@@ -647,6 +647,11 @@ local commands={}do
|
||||
scene='app_triple',
|
||||
description="A Match-3 Game. Original idea from Sanlitun / Triple Town"
|
||||
},
|
||||
{
|
||||
code="sw",
|
||||
scene='app_stopwatch',
|
||||
description="A stopwatch"
|
||||
},
|
||||
{
|
||||
code="spin",
|
||||
scene='app_spin',
|
||||
@@ -862,7 +867,7 @@ local commands={}do
|
||||
--Network
|
||||
commands.switchhost={
|
||||
code=function(arg)
|
||||
arg=STRING.split(arg," ")
|
||||
arg=arg:split(" ")
|
||||
if arg[1]and #arg<=3 then
|
||||
WS.switchHost(unpack(arg))
|
||||
log{C.Y,"Host switched"}
|
||||
|
||||
@@ -174,7 +174,7 @@ function scene.update(dt)
|
||||
end
|
||||
end
|
||||
|
||||
life1=life1*.7+life*.3
|
||||
life1=MATH.expApproach(life1,life,dt*16)
|
||||
|
||||
if play then
|
||||
if inv>0 then
|
||||
|
||||
@@ -280,13 +280,13 @@ function scene.touchDown(x)
|
||||
end
|
||||
end
|
||||
|
||||
function scene.update()
|
||||
function scene.update(dt)
|
||||
if state==1 then
|
||||
local t=TIME()
|
||||
time=t-startTime
|
||||
local v=0
|
||||
for i=2,20 do v=v+i*(i-1)*.3/(t-keyTime[i])end
|
||||
speed=speed*.99+v*.01
|
||||
speed=MATH.expApproach(speed,v,dt)
|
||||
if speed>maxSpeed then maxSpeed=speed end
|
||||
|
||||
if arcade then
|
||||
|
||||
70
parts/scenes/app_stopwatch.lua
Normal file
70
parts/scenes/app_stopwatch.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
local state
|
||||
local pressTime
|
||||
local releaseTime
|
||||
local time1,time2
|
||||
|
||||
local function press()
|
||||
if state==0 then
|
||||
state=1
|
||||
pressTime=TIME()
|
||||
releaseTime=false
|
||||
time2=STRING.time(0)
|
||||
elseif state==2 then
|
||||
state=0
|
||||
end
|
||||
end
|
||||
|
||||
local function release()
|
||||
if state==1 then
|
||||
state=2
|
||||
releaseTime=TIME()
|
||||
end
|
||||
end
|
||||
|
||||
local scene={}
|
||||
|
||||
function scene.sceneInit()
|
||||
state=0
|
||||
time1=STRING.time(0)
|
||||
time2=STRING.time(0)
|
||||
end
|
||||
|
||||
function scene.mouseDown()
|
||||
press()
|
||||
end
|
||||
function scene.mouseUp()
|
||||
release()
|
||||
end
|
||||
function scene.keyDown(key,isRep)
|
||||
if isRep then return end
|
||||
if key=='escape'then
|
||||
SCN.back()
|
||||
else
|
||||
press()
|
||||
end
|
||||
end
|
||||
function scene.keyUp()
|
||||
release()
|
||||
end
|
||||
|
||||
function scene.update()
|
||||
if state~=0 then
|
||||
time1=STRING.time(TIME()-pressTime)
|
||||
if releaseTime then
|
||||
time2=STRING.time(TIME()-releaseTime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function scene.draw()
|
||||
FONT.set(60)
|
||||
mStr(CHAR.icon.import,340,230)
|
||||
mStr(CHAR.icon.export,940,230)
|
||||
mStr(time1,340,300)
|
||||
mStr(time2,940,300)
|
||||
end
|
||||
|
||||
scene.widgetList={
|
||||
WIDGET.newButton{name="back", x=1140,y=640,w=170,h=80,font=60,fText=CHAR.icon.back,code=backScene},
|
||||
}
|
||||
return scene
|
||||
@@ -31,11 +31,11 @@ function scene.keyDown(key,isRep)
|
||||
end
|
||||
end
|
||||
|
||||
function scene.update()
|
||||
function scene.update(dt)
|
||||
local t=TIME()
|
||||
local v=0
|
||||
for i=2,40 do v=v+i*(i-1)*.075/(t-keyTime[i])end
|
||||
speed=speed*.99+v*.01
|
||||
speed=MATH.expApproach(speed,v,dt)
|
||||
if speed>maxSpeed then
|
||||
maxSpeed=speed
|
||||
end
|
||||
|
||||
@@ -103,7 +103,7 @@ function scene.keyDown(key,isRep)
|
||||
MES.new('check',text.exportSuccess)
|
||||
elseif key=='v'and kb.isDown('lctrl','rctrl')or key=='cV'then
|
||||
local str=sys.getClipboardText()
|
||||
local args=STRING.split(str:sub((str:find(":")or 0)+1),"!")
|
||||
local args=str:sub((str:find(":")or 0)+1):split("!")
|
||||
if #args<4 then goto THROW_fail end
|
||||
if not(
|
||||
DATA.pasteQuestArgs(args[1])and
|
||||
|
||||
@@ -9,9 +9,9 @@ local scene={}
|
||||
|
||||
function scene.sceneInit()
|
||||
BGcolor=rnd()>.026 and{.3,.5,.9}or{.62,.3,.926}
|
||||
sysAndScn=SYSTEM.."-"..VERSION.string.." scene:"..Z.errData[#Z.errData].scene
|
||||
sysAndScn=SYSTEM.."-"..VERSION.string.." scene:"..Z.getErr('#').scene
|
||||
errorText=LOADED and text.errorMsg or"An error has occurred while the game was loading.\nAn error log has been created so you can send it to the author."
|
||||
errorShot,errorInfo=Z.errData[#Z.errData].shot,Z.errData[#Z.errData].mes
|
||||
errorShot,errorInfo=Z.getErr('#').shot,Z.getErr('#').mes
|
||||
NET.wsclose_app()
|
||||
NET.wsclose_user()
|
||||
NET.wsclose_play()
|
||||
|
||||
@@ -298,7 +298,7 @@ local function _update_common(dt)
|
||||
end
|
||||
|
||||
--Warning check
|
||||
checkWarning()
|
||||
checkWarning(dt)
|
||||
end
|
||||
function scene.update(dt)
|
||||
trigGameRate=trigGameRate+gameRate
|
||||
|
||||
@@ -2,7 +2,7 @@ local scene={}
|
||||
|
||||
function scene.sceneInit()
|
||||
BG.set('cubes')
|
||||
WIDGET.active.texts:setTexts(STRING.split(require"parts.updateLog","\n"))
|
||||
WIDGET.active.texts:setTexts(require"parts.updateLog":split("\n"))
|
||||
end
|
||||
|
||||
function scene.wheelMoved(_,y)
|
||||
|
||||
@@ -42,7 +42,7 @@ end
|
||||
|
||||
local function _setLang(lid)
|
||||
SETTING.locale=lid
|
||||
applyLanguage()
|
||||
applySettings()
|
||||
TEXT.clear()
|
||||
TEXT.show(langList[lid],640,360,100,'appear',.626)
|
||||
collectgarbage()
|
||||
|
||||
@@ -3,7 +3,7 @@ function scene.sceneInit()
|
||||
BG.set('cubes')
|
||||
local fileData=love.filesystem.read("legals.md")
|
||||
if fileData then
|
||||
WIDGET.active.texts:setTexts(STRING.split(fileData,'\n'))
|
||||
WIDGET.active.texts:setTexts(fileData:split('\n'))
|
||||
else
|
||||
WIDGET.active.texts:setTexts{"[legals.md not found]"}
|
||||
end
|
||||
|
||||
@@ -113,6 +113,7 @@ local loadingThread=coroutine.wrap(function()
|
||||
VOC.play('welcome')
|
||||
THEME.fresh()
|
||||
LOADED=true
|
||||
Z.setPowerInfo(SETTING.powerInfo)
|
||||
return'finish'
|
||||
end)
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ function scene.update(dt)
|
||||
end
|
||||
local L=scene.widgetList
|
||||
for i=1,8 do
|
||||
L[i].x=L[i].x*.9+(widgetX0[i]-400+(WIDGET.isFocus(L[i])and(i<5 and 100 or -100)or 0))*.1
|
||||
L[i].x=MATH.expApproach(L[i].x,(widgetX0[i]-400+(WIDGET.isFocus(L[i])and(i<5 and 100 or -100)or 0)),dt*12)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ function scene.sceneInit()
|
||||
BG.set('cubes')
|
||||
local fileData=love.filesystem.read('parts/language/manual_'..(SETTING.locale:find'zh'and'zh'or'en')..'.txt')
|
||||
if fileData then
|
||||
WIDGET.active.texts:setTexts(STRING.split(fileData,'\n'))
|
||||
WIDGET.active.texts:setTexts(fileData:split('\n'))
|
||||
else
|
||||
WIDGET.active.texts:setTexts{"[manual file not found]"}
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ local function _toggleMod(M,back)
|
||||
_remMod(M)
|
||||
end
|
||||
if M.unranked then
|
||||
SFX.play('move',.6)
|
||||
SFX.play('touch',.6)
|
||||
SFX.play('lock')
|
||||
else
|
||||
SFX.play('touch')
|
||||
|
||||
@@ -162,7 +162,7 @@ function scene.update()
|
||||
if kb.isDown('down', 's')then dy=dy-10 F=true end
|
||||
if kb.isDown('left', 'a')then dx=dx+10 F=true end
|
||||
if kb.isDown('right','d')then dx=dx-10 F=true end
|
||||
local js=Z.js[1]
|
||||
local js=Z.getJsState()[1]
|
||||
if js then
|
||||
local sx,sy=js._jsObj:getGamepadAxis('leftx'),js._jsObj:getGamepadAxis('lefty')
|
||||
if math.abs(sx)>.1 or math.abs(sy)>.1 then
|
||||
|
||||
@@ -44,12 +44,12 @@ function scene.keyDown(key,isRep)
|
||||
if key=='down'then
|
||||
if S<#bgmList then
|
||||
selected=S+1
|
||||
SFX.play('move',.7)
|
||||
SFX.play('touch',.7)
|
||||
end
|
||||
elseif key=='up'then
|
||||
if S>1 then
|
||||
selected=S-1
|
||||
SFX.play('move',.7)
|
||||
SFX.play('touch',.7)
|
||||
end
|
||||
elseif not isRep then
|
||||
if key=='return'or key=='space'then
|
||||
|
||||
@@ -246,7 +246,7 @@ function scene.update(dt)
|
||||
for p=1,#PLAYERS do PLAYERS[p]:update(dt)end
|
||||
|
||||
--Warning check
|
||||
checkWarning()
|
||||
checkWarning(dt)
|
||||
|
||||
--Upload stream
|
||||
if not NET.spectate and P1.frameRun-lastUpstreamTime>8 then
|
||||
@@ -265,7 +265,7 @@ function scene.update(dt)
|
||||
lastUpstreamTime=PLAYERS[1].alive and P1.frameRun or 1e99
|
||||
end
|
||||
else
|
||||
NETPLY.update()
|
||||
NETPLY.update(dt)
|
||||
end
|
||||
if newMessageTimer>0 then
|
||||
newMessageTimer=newMessageTimer-1
|
||||
|
||||
@@ -47,7 +47,7 @@ function scene.sceneInit()
|
||||
(S.atk+S.dig)/S.time*60,--ADPM
|
||||
S.atk/S.time*60, --AtkPM
|
||||
S.send/S.time*60, --SendPM
|
||||
S.piece/S.time*24, --LinePM
|
||||
S.piece/S.time*24, --Line'PM
|
||||
S.dig/S.time*60, --DigPM
|
||||
}
|
||||
val={1/80,1/80,1/80,1/60,1/100,1/40}
|
||||
|
||||
@@ -10,8 +10,8 @@ function scene.sceneInit()
|
||||
end
|
||||
else
|
||||
MES.update(1e99)
|
||||
SETTING.powerInfo=false
|
||||
SETTING.tapFX=false
|
||||
Z.setPowerInfo(false)
|
||||
Z.setClickFX(false)
|
||||
VERSION.string=""
|
||||
MES.new('error',"Please swipe up or press Home button to quit Techmino on iOS",1e99)
|
||||
end
|
||||
|
||||
@@ -139,8 +139,8 @@ end
|
||||
|
||||
scene.widgetList={
|
||||
listBox,
|
||||
WIDGET.newButton{name='export',x=180,y=640,w=140,h=80,color='lG',code=pressKey'cC',font=50,fText=CHAR.icon.export},
|
||||
WIDGET.newButton{name='import',x=350,y=640,w=140,h=80,color='lN',code=pressKey'cV',font=50,fText=CHAR.icon.import},
|
||||
WIDGET.newButton{name='import',x=180,y=640,w=140,h=80,color='lB',code=pressKey'cV',font=50,fText=CHAR.icon.import},
|
||||
WIDGET.newButton{name='export',x=350,y=640,w=140,h=80,color='lR',code=pressKey'cC',font=50,fText=CHAR.icon.export},
|
||||
WIDGET.newButton{name='play', x=700,y=640,w=170,h=80,color='lY',code=pressKey'return',font=65,fText=CHAR.icon.play},
|
||||
WIDGET.newButton{name='delete',x=850,y=640,w=80,h=80,color='lR',code=pressKey'delete',font=50,fText=CHAR.icon.trash},
|
||||
WIDGET.newButton{name='back', x=1140,y=640,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene},
|
||||
|
||||
@@ -59,7 +59,7 @@ scene.widgetList={
|
||||
local D=_parseCB()
|
||||
if D then
|
||||
TABLE.update(D,SETTING)
|
||||
applyAllSettings()
|
||||
applySettings()
|
||||
saveSettings()
|
||||
MES.new('check',text.importSuccess)
|
||||
else
|
||||
|
||||
@@ -45,11 +45,11 @@ scene.widgetList={
|
||||
WIDGET.newSlider{name='reTime', x=330, y=320, w=300,lim=180,unit=10,disp=SETval('reTime'), code=SETsto('reTime'),show=function(S)return(.5+S.disp()*.25).."s"end},
|
||||
WIDGET.newSelector{name='RS', x=300, y=420, w=300,color='S', disp=SETval('RS'), code=SETsto('RS'),list={'TRS','SRS','SRS_plus','SRS_X','BiRS','ARS_Z','DRS_weak','ASC','ASC_plus','C2','C2_sym','Classic','Classic_plus','None','None_plus'}},
|
||||
WIDGET.newSelector{name='menuPos',x=980, y=320, w=300,color='O', disp=SETval('menuPos'), code=SETsto('menuPos'),list={'left','middle','right'}},
|
||||
WIDGET.newSwitch{name='sysCursor',x=1060, y=390, lim=580, disp=SETval('sysCursor'),code=function()SETTING.sysCursor=not SETTING.sysCursor applyCursor()end},
|
||||
WIDGET.newSwitch{name='autoPause',x=1060, y=450, lim=580, disp=SETval('autoPause'),code=SETrev('autoPause')},
|
||||
WIDGET.newSwitch{name='autoSave', x=1060, y=500, lim=580, disp=SETval('autoSave'), code=SETrev('autoSave')},
|
||||
WIDGET.newSwitch{name='autoLogin',x=960, y=580, lim=480, disp=SETval('autoLogin'),code=SETrev('autoLogin')},
|
||||
WIDGET.newSwitch{name='simpMode', x=960, y=640, lim=480, disp=SETval('simpMode'),
|
||||
WIDGET.newSwitch{name='sysCursor',x=1060, y=400, lim=580, disp=SETval('sysCursor'),code=function()SETTING.sysCursor=not SETTING.sysCursor applySettings()end},
|
||||
WIDGET.newSwitch{name='autoPause',x=1060, y=470, lim=580, disp=SETval('autoPause'),code=SETrev('autoPause')},
|
||||
WIDGET.newSwitch{name='autoSave', x=1060, y=540, lim=580, disp=SETval('autoSave'), code=SETrev('autoSave')},
|
||||
WIDGET.newSwitch{name='autoLogin',x=960, y=610, lim=480, disp=SETval('autoLogin'),code=SETrev('autoLogin')},
|
||||
WIDGET.newSwitch{name='simpMode', x=960, y=670, lim=480, disp=SETval('simpMode'),
|
||||
code=function()
|
||||
SETTING.simpMode=not SETTING.simpMode
|
||||
for i=1,#SCN.stack,2 do
|
||||
|
||||
@@ -22,9 +22,9 @@ function scene.sceneInit()
|
||||
end
|
||||
end
|
||||
|
||||
function scene.update()
|
||||
function scene.update(dt)
|
||||
for i=1,7 do
|
||||
minoRot[i]=minoRot[i]*.8+minoRot0[i]*.2
|
||||
minoRot[i]=MATH.expApproach(minoRot[i],minoRot0[i],dt*12)
|
||||
end
|
||||
end
|
||||
function scene.draw()
|
||||
|
||||
@@ -87,7 +87,7 @@ scene.widgetList={
|
||||
WIDGET.newSlider{name='mainVol', x=300, y=170,w=420,lim=220,color='lG',disp=SETval('mainVol'), code=function(v)SETTING.mainVol=v love.audio.setVolume(SETTING.mainVol)end},
|
||||
WIDGET.newSlider{name='bgm', x=300, y=240,w=420,lim=220,color='lG',disp=SETval('bgm'), code=function(v)SETTING.bgm=v BGM.setVol(SETTING.bgm)end},
|
||||
WIDGET.newSlider{name='sfx', x=300, y=310,w=420,lim=220,color='lC',disp=SETval('sfx'), code=function(v)SETTING.sfx=v SFX.setVol(SETTING.sfx)end, change=function()SFX.play('warn_1')end},
|
||||
WIDGET.newSlider{name='stereo', x=300, y=380,w=420,lim=220,color='lC',disp=SETval('stereo'), code=function(v)SETTING.stereo=v SFX.setStereo(SETTING.stereo)end,change=function()SFX.play('move',1,-1)SFX.play('lock',1,1)end,hideF=function()return SETTING.sfx==0 end},
|
||||
WIDGET.newSlider{name='stereo', x=300, y=380,w=420,lim=220,color='lC',disp=SETval('stereo'), code=function(v)SETTING.stereo=v SFX.setStereo(SETTING.stereo)end,change=function()SFX.play('touch',1,-1)SFX.play('lock',1,1)end,hideF=function()return SETTING.sfx==0 end},
|
||||
WIDGET.newSlider{name='spawn', x=300, y=450,w=420,lim=220,color='lC',disp=SETval('sfx_spawn'), code=function(v)SETTING.sfx_spawn=v end, change=function()SFX.fplay('spawn_'..math.random(7),SETTING.sfx_spawn)end,},
|
||||
WIDGET.newSlider{name='warn', x=300, y=520,w=420,lim=220,color='lC',disp=SETval('sfx_warn'), code=function(v)SETTING.sfx_warn=v end, change=function()SFX.fplay('warn_beep',SETTING.sfx_warn)end},
|
||||
WIDGET.newSlider{name='vib', x=300, y=590,w=420,lim=220,color='lN',disp=SETval('vib'),unit=10,code=function(v)SETTING.vib=v end, change=function()if SETTING.vib>0 then VIB(SETTING.vib+2)end end},
|
||||
|
||||
@@ -11,7 +11,7 @@ function scene.fileDropped(file)
|
||||
if pcall(gc.newImage,file)then
|
||||
love.filesystem.write('conf/customBG',file:read('data'))
|
||||
SETTING.bg='custom'
|
||||
applyBG()
|
||||
applySettings()
|
||||
else
|
||||
MES.new('error',text.customBGloadFailed)
|
||||
end
|
||||
@@ -68,7 +68,7 @@ scene.widgetList={
|
||||
WIDGET.newSlider{name='shakeFX', x=330,y=760,lim=280,w=540,unit=5,disp=SETval('shakeFX'), code=SETsto('shakeFX')},
|
||||
WIDGET.newSlider{name='atkFX', x=330,y=820,lim=280,w=540,unit=5,disp=SETval('atkFX'), code=SETsto('atkFX')},
|
||||
|
||||
WIDGET.newSelector{name='frame', x=400,y=890,lim=280,w=460,list={8,10,13,17,22,29,37,47,62,80,100},disp=SETval('frameMul'),code=SETsto('frameMul')},
|
||||
WIDGET.newSelector{name='frame', x=400,y=890,lim=280,w=460,list={8,10,13,17,22,29,37,47,62,80,100},disp=SETval('frameMul'),code=function(v)SETTING.frameMul=v;Z.setFrameMul(SETTING.frameMul)end},
|
||||
WIDGET.newSwitch{name='FTlock', x=950,y=890,lim=290,disp=SETval('FTLock'), code=SETrev('FTLock')},
|
||||
|
||||
WIDGET.newSwitch{name='text', x=450,y=980,lim=360,disp=SETval('text'), code=SETrev('text')},
|
||||
@@ -79,18 +79,18 @@ scene.widgetList={
|
||||
WIDGET.newSwitch{name='highCam', x=450,y=1270,lim=360,disp=SETval('highCam'), code=SETrev('highCam')},
|
||||
WIDGET.newSwitch{name='warn', x=450,y=1340,lim=360,disp=SETval('warn'), code=SETrev('warn')},
|
||||
|
||||
WIDGET.newSwitch{name='clickFX', x=950,y=980,lim=360,disp=SETval('clickFX'), code=SETrev('clickFX')},
|
||||
WIDGET.newSwitch{name='power', x=950,y=1070,lim=360,disp=SETval('powerInfo'), code=SETrev('powerInfo')},
|
||||
WIDGET.newSwitch{name='clean', x=950,y=1160,lim=360,disp=SETval('cleanCanvas'), code=SETrev('cleanCanvas')},
|
||||
WIDGET.newSwitch{name='fullscreen', x=950,y=1250,lim=360,disp=SETval('fullscreen'), code=function()SETTING.fullscreen=not SETTING.fullscreen applyFullscreen()end},
|
||||
WIDGET.newSwitch{name='clickFX', x=950,y=980,lim=360,disp=SETval('clickFX'), code=function()SETTING.clickFX=not SETTING.clickFX applySettings()end},
|
||||
WIDGET.newSwitch{name='power', x=950,y=1070,lim=360,disp=SETval('powerInfo'), code=function()SETTING.powerInfo=not SETTING.powerInfo applySettings()end},
|
||||
WIDGET.newSwitch{name='clean', x=950,y=1160,lim=360,disp=SETval('cleanCanvas'), code=function()SETTING.cleanCanvas=not SETTING.cleanCanvas applySettings()end},
|
||||
WIDGET.newSwitch{name='fullscreen', x=950,y=1250,lim=360,disp=SETval('fullscreen'), code=function()SETTING.fullscreen=not SETTING.fullscreen applySettings()end},
|
||||
|
||||
WIDGET.newKey{name='bg_on', x=680,y=1340,w=200,h=80,code=function()SETTING.bg='on'applyBG()end},
|
||||
WIDGET.newKey{name='bg_off', x=900,y=1340,w=200,h=80,code=function()SETTING.bg='off'applyBG()end},
|
||||
WIDGET.newKey{name='bg_on', x=680,y=1340,w=200,h=80,code=function()SETTING.bg='on'applySettings()end},
|
||||
WIDGET.newKey{name='bg_off', x=900,y=1340,w=200,h=80,code=function()SETTING.bg='off'applySettings()end},
|
||||
WIDGET.newKey{name='bg_custom', x=1120,y=1340,w=200,h=80,
|
||||
code=function()
|
||||
if love.filesystem.getInfo('conf/customBG')then
|
||||
SETTING.bg='custom'
|
||||
applyBG()
|
||||
applySettings()
|
||||
else
|
||||
MES.new('info',text.customBGhelp)
|
||||
end
|
||||
@@ -105,12 +105,12 @@ scene.widgetList={
|
||||
WIDGET.newSelector{name='blockSatur', x=800,y=1440,w=300,color='lN',
|
||||
list={'normal','soft','gray','light','color'},
|
||||
disp=SETval('blockSatur'),
|
||||
code=function(v)SETTING.blockSatur=v;applyBlockSatur(SETTING.blockSatur)end
|
||||
code=function(v)SETTING.blockSatur=v;applySettings(SETTING.blockSatur)end
|
||||
},
|
||||
WIDGET.newSelector{name='fieldSatur', x=800,y=1540,w=300,color='lN',
|
||||
list={'normal','soft','gray','light','color'},
|
||||
disp=SETval('fieldSatur'),
|
||||
code=function(v)SETTING.fieldSatur=v;applyFieldSatur(SETTING.fieldSatur)end
|
||||
code=function(v)SETTING.fieldSatur=v;applySettings(SETTING.fieldSatur)end
|
||||
},
|
||||
|
||||
WIDGET.newButton{name='back', x=1140,y=640,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene},
|
||||
|
||||
@@ -2,8 +2,8 @@ local THEME={
|
||||
cur=false,--Current theme
|
||||
}
|
||||
local themeColor={
|
||||
xmas={COLOR.R,COLOR.Z,COLOR.G},
|
||||
sprfes={COLOR.R,COLOR.O,COLOR.Y},
|
||||
xmas={COLOR.lR,COLOR.Z,COLOR.lG},
|
||||
sprfes={COLOR.lR,COLOR.O,COLOR.lY},
|
||||
}
|
||||
|
||||
function THEME.calculate(Y,M,D)
|
||||
|
||||
@@ -9,7 +9,7 @@ return[=[
|
||||
等级系统; 场地格边缘线; 模式数据分析; 高级自定义序列
|
||||
成就系统; 手势操作; C2连击; 特殊控件(虚拟摇杆等)
|
||||
组队战; 方块位移/旋转动画; 更细节的DAS选项; 拓展主题系统
|
||||
更自由的攻击系统; 更多消除方式; 可调场地宽度;
|
||||
更自由的攻击系统; 更多消除方式; 可调场地宽度;
|
||||
task-Z(新AI); 自适应UI; 新联网游戏场景切换逻辑
|
||||
|
||||
0.17.1: 苏醒 Wake Up
|
||||
@@ -17,19 +17,33 @@ return[=[
|
||||
全新模式选择界面(未完善,未来会增加更多便利功能)
|
||||
新皮肤:pixel(by C₂₉H₂₅N₃O₅)
|
||||
新语言:Bahasa Indonesia(by NOT_A_ROBOT)
|
||||
新小程序:Stopwatch
|
||||
改动:
|
||||
修改选择模式音效
|
||||
优化皮肤设置页面交互效果
|
||||
换新miya立绘,给不同立绘添加不同点击动画
|
||||
由于一些无关紧要的小原因暂时移除一个语音包
|
||||
略微降低master-h模式骨块出现后的难度
|
||||
TRS的N/H添加一个踢墙
|
||||
X块的默认色改为黄色
|
||||
重做关于页面
|
||||
重做关于页面,微调设置菜单
|
||||
微调全局颜色表
|
||||
修改一行HPC的判定
|
||||
前两个TSD增加一点重力
|
||||
混战模式AI攻击目标稍微正常一些
|
||||
两个有颜色的节日主题颜色浅一些
|
||||
代码:
|
||||
调整帧率控制算法
|
||||
BGM模块允许瞬启停
|
||||
SFX模块立体声优化
|
||||
升级主循环帧率控制
|
||||
框架更加可独立
|
||||
修复:
|
||||
自定义场地界面按超过第三个的鼠标键会报错
|
||||
经典模式h和u难度没有干旱计数器 #546
|
||||
自定义场地16号色的方块名位置显示错误
|
||||
联网对战结算的l'pm公式错写成lpm的
|
||||
录像界面导入导出按钮隐藏状态错误
|
||||
登录界面读取本地账号密码数据错误
|
||||
策略堆叠模式评级标准不当
|
||||
云存档/读档的一处小问题
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
return{
|
||||
["apkCode"]=421,
|
||||
["apkCode"]=423,
|
||||
["code"]=1701,
|
||||
["string"]="V0.17.1",
|
||||
["room"]="ver A-2",
|
||||
|
||||
Reference in New Issue
Block a user