Merge Electra's enhanced IRS

This commit is contained in:
MrZ_26
2024-09-03 21:12:32 +08:00
parent 9672a4fe57
commit 7407911914
15 changed files with 203 additions and 50 deletions

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=6,sdarr=6,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=6,lock=6,
wait=10,fall=25,
freshLimit=0,

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=3,sdarr=3,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=3,lock=3,
wait=10,fall=25,
freshLimit=0,

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=2,sdarr=2,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=2,lock=2,
wait=10,fall=25,
freshLimit=0,

View File

@@ -7,7 +7,7 @@ end
return {
das=16,arr=6,
sddas=1,sdarr=1,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=1,lock=1,
wait=10,fall=25,
freshLimit=0,

View File

@@ -149,7 +149,7 @@ return {
keyCancel={10,11,12,14,15,16,17,18,19,20},
das=16,arr=1,
minsdarr=1,
ihs=true,irs=true,ims=false,
logicalIRS=true,logicalIHS=true,logicalIMS=false,
mesDisp=function(P)
local D=P.modeData
GC.setColor(1,1,1,1)

View File

@@ -975,7 +975,7 @@ end
do-- function dumpBasicConfig()
local gameSetting={
-- Tuning
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
-- System
@@ -1021,7 +1021,7 @@ do-- function resetGameData(args)
end
local gameSetting={
-- Tuning
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
-- System
@@ -1236,7 +1236,7 @@ do-- function pressKey(k)
end
do-- SETXXX(k) & ROOMXXX(k)
local warnList={
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
'frameMul','highCam',
'VKSwitch','VKIcon','VKTrack','VKDodge',

View File

@@ -605,7 +605,7 @@ do-- Userdata tables
SETTING={-- Settings
-- Tuning
das=10,arr=2,
dascut=0,dropcut=0,
dascut=0,irscut=6,dropcut=0,
sddas=0,sdarr=2,
ihs=true,irs=true,ims=true,
holdMode='hold',

View File

@@ -865,6 +865,11 @@ FNNS and {"Support 3",
"term",
"A special delay applied to DAS when a new block is spawned. When this happens, a small delay is added before the DAS starts timing, so that a piece doesn't start moving immediately when a sideways direction key is pressed.\nOther games may have similar mechanisms, but they may work differently.",
},
{"IRS cut",
"irscut icd",
"term",
"A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
},
{"Auto-lock cut",
"autolockcut mdcut",
"term",

View File

@@ -961,6 +961,11 @@ FNNS and {"サポート3",
"term",
"*Techmino用語*通常、ミが出現する前にDAS時間以上入力をしているとミが出現した瞬間に動き出します\nDASカットはこのような現象を減らすためにDAS時間以上入力していても出現時にDASカット分減算する機能です\n他のゲームにも似たようなものがありますが恐らく異なるでしょう",
},
-- {"IRS cut",
-- "irscut icd",
-- "term",
-- "A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
-- },
{"Auto-lock cut(自動設置カット)",
"autolockcut mdcut 自動 カット",
"term",

View File

@@ -654,6 +654,11 @@ Xem mục tiếp theo để biết thêm.
"term",
"Cơ chế đặc biệt sẽ được kích hoạt khi gạch mới xuất hiện. Khi kích hoạt, cơ chế này sẽ tăng DAS lên một chút để gạch không tự di chuyển ngay khi đang có phím được giữ.\n\nCác game khác có thể có tính năng tương tự nhưng cách hoạt động có thể khác nhau.",
},
-- {"IRS cut",
-- "irscut icd",
-- "term",
-- "A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
-- },
{"Auto-lock cut",
"nhom05e2 autolockcut",
"term",

View File

@@ -853,6 +853,11 @@ FNNS and {"赞助3",
"term",
"Techmino中指玩家的操作焦点转移到新方块的瞬间此时减小重置DAS计时器让自动移动不会立刻生效减少 “移动键松开晚了导致下一块一出来就立即开始移动” 的情况\n其他游戏中的DAS打断机制可能和Techmino的有区别仅供参考。",
},
{"IRS打断(ICD)",
"irscut icd daduan",
"term",
"由Electra设计新方块生成时触发IRS的特殊延迟。在没有生成延迟时这会让预输入的旋转动作延迟一段时间再生效允许晚一点松开旋转键防止md。",
},
{"误硬降打断(HCD)",
"autolockcut mdcut daduan",
"term",

View File

@@ -622,6 +622,7 @@ C. Gamepad
das="DAS",arr="ARR",
dascut="DAS Cut",
irscut="IRS Cut",
dropcut="Auto-lock Cut",
sddas="Soft Drop DAS",sdarr="Soft Drop ARR",
ihs="Initial Hold",

View File

@@ -1,8 +1,9 @@
return {
das=10,arr=2,
dascut=0,dropcut=0,
dascut=0,irscut=6,dropcut=0,
sddas=2,sdarr=2,
ihs=true,irs=true,ims=true,
logicalIHS=true,logicalIRS=true,logicalIMS=true,
ghostType='gray',
block=true,ghost=.3,center=1,

View File

@@ -270,27 +270,61 @@ function Player:act_rotRight()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+1
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[3]=false
self:resolveIRS()
self.keyPressing[3]=true
end
self:spin(1)
self:_triggerEvent('hook_rotate',1)
self.keyPressing[3]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[3]=false
end
end
end
function Player:act_rotLeft()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+1
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[4]=false
self:resolveIRS()
self.keyPressing[4]=true
end
self:spin(3)
self:_triggerEvent('hook_rotate',3)
self.keyPressing[4]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[4]=false
end
end
end
function Player:act_rot180()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+2
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[5]=false
self:resolveIRS()
self.keyPressing[5]=true
end
self:spin(2)
self:_triggerEvent('hook_rotate',2)
self.keyPressing[5]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[5]=false
end
end
end
function Player:act_hardDrop()
@@ -300,6 +334,10 @@ function Player:act_hardDrop()
if self.lastPiece.autoLock and self.frameRun-self.lastPiece.frame<ENV.dropcut then
SFX.play('drop_cancel',.3)
else
if self.bufferedIRS then
-- If the player drops quicker than their IRS cut delay, make sure IRS still resolves.
self:resolveIRS()
end
if self.curY>self.ghoY then
self:createDropFX()
self.curY=self.ghoY
@@ -344,7 +382,10 @@ function Player:act_hold()
if not self.control then return end
if self.cur then
if self:hold() then
self.keyPressing[8]=false
-- Disable held inputs if IHS is off
if not self.gameEnv.ihs then
self.keyPressing[8]=false
end
self:_triggerEvent('hook_hold')
end
end
@@ -1164,34 +1205,67 @@ function Player:resetBlock()-- Reset Block's position and execute I*S
self.curY=y
self.minY=y+sc[1]
local ENV=self.gameEnv
-- In the game settings, there are user-set control flags for irs,irs,ims
-- These control in what way the user can buffer their rotate/hold/move inputs.
-- (If enabled, they may hold these inputs from the previous piece instead of just Entry Delay)
-- And mode-set flags for logicalIRS,logicalIHS,logicalIMS
-- These control whether IRS/IHS/IMS are effective in modifying what you can do.
-- (For instance, changing your piece's spawn position in 20g, or saving you from a death).
-- If logical IRS is disabled, the player may still IRS, but it will just buffer their input,
-- not actually allowing them to survive in a way they could not without.
local pressing=self.keyPressing
-- IMS
if self.gameEnv.ims and (pressing[1] and self.movDir==-1 or pressing[2] and self.movDir==1) and self.moving>=self.gameEnv.das then
local x=self.curX+self.movDir
if not self:ifoverlap(C.bk,x,y) then
self.curX=x
-- IMS is enabled only when logicalIMS is enabled, because otherwise, it's just faster DAS.
if ENV.logicalIMS and (pressing[1] and self.movDir==-1 or pressing[2] and self.movDir==1) and self.moving>=self.gameEnv.das then
-- To avoid a top-out
if self:ifoverlap(C.bk,self.curX,self.curY) then
-- Always perform the shift, since you're topped out anyway
self.curX=self.curX+self.movDir
elseif ENV.wait>0 and ENV.ims then
-- Otherwise, only check IMS if it's enabled and you're in a mode with entry delay (20g)
local x=self.curX+self.movDir
if not self:ifoverlap(C.bk,x,y) then
self.curX=x
end
end
end
-- IRS
if self.gameEnv.irs then
if not ENV.logicalIRS then
-- If logical IRS is disabled, all IRS inputs will be buffered to prevent survival.
self.bufferedIRS=true
self.bufferedDelay=0
if ENV.wait==0 then
self.bufferedDelay=ENV.irscut
end
elseif ENV.wait==0 and ENV.irscut>0 and not self:ifoverlap(C.bk,self.curX,self.curY) then
-- If IRS cut delay is enabled and we aren't currently dying, buffer the input instead.
self.bufferedIRS=true
self.bufferedDelay=ENV.irscut
else
-- If we're currently dying or in an entry-delay mode (20g), perform the rotation right away.
if pressing[5] then
self:spin(2,true)
self:act_rot180()
elseif pressing[3] then
if pressing[4] then
self:spin(2,true)
self:act_rot180()
else
self:spin(1,true)
self:act_rotRight()
end
elseif pressing[4] then
self:spin(3,true)
self:act_rotLeft()
end
end
-- Disable held inputs if IRS is off
if not ENV.irs then
pressing[3],pressing[4],pressing[5]=false,false,false
end
-- DAS cut
if self.gameEnv.dascut>0 then
self.moving=self.moving-(self.moving>0 and 1 or -1)*self.gameEnv.dascut
if ENV.dascut>0 then
self.moving=self.moving-(self.moving>0 and 1 or -1)*ENV.dascut
end
-- Spawn SFX
@@ -1511,9 +1585,29 @@ function Player:_popNext(ifhold)-- Pop nextQueue to hand
local pressing=self.keyPressing
-- IHS
if not ifhold and pressing[8] and ENV.ihs and self.holdTime>0 then
self:hold(true)
pressing[8]=false
if not ifhold and pressing[8] and self.holdTime>0 then
if not ENV.logicalIHS then
-- If logical IHS is disabled, all IHS inputs will be buffered to prevent survival.
self.bufferedIRS=true
self.bufferedIHS=true
self.bufferedDelay=0
if ENV.wait==0 then
self.bufferedDelay=ENV.irscut
end
elseif ENV.wait==0 and ENV.irscut>0 and not self:willDieWith(self.cur) then
-- If IRS cut delay is enabled and we're not currently dying, buffer the input instead.
self.bufferedIRS=true
self.bufferedIHS=true
self.bufferedDelay=ENV.irscut
self:resetBlock()
else
-- If we're currently dying or in an entry-delay mode (20g), perform the hold immediately.
self:hold(true)
end
-- Disable held inputs if IHS is off
if not ENV.ihs then
pressing[8]=false
end
else
self:resetBlock()
end
@@ -1827,7 +1921,7 @@ do
end
end
local yomi = ""
local yomi=''
piece.spin,piece.mini=dospin,false
piece.pc,piece.hpc=false,false
@@ -1838,7 +1932,7 @@ do
cscore=(spinSCR[C.name] or spinSCR[8])[cc]
if self.b2b>800 then
self:showText(text.b3b..text.block[C.name]..text.spin..text.clear[cc],0,-30,35,'stretch')
yomi = yomi..text.b3b..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.b3b..text.block[C.name]..text.spin..text.clear[cc]
atk=b2bATK[cc]+cc*.5
exblock=exblock+1
cscore=cscore*2
@@ -1848,7 +1942,7 @@ do
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.block[C.name]..text.spin..text.clear[cc],0,-30,35,'spin')
yomi = yomi..text.b2b..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.b2b..text.block[C.name]..text.spin..text.clear[cc]
atk=b2bATK[cc]
cscore=cscore*1.2
Stat.b2b=Stat.b2b+1
@@ -1857,13 +1951,13 @@ do
end
else
self:showText(text.block[C.name]..text.spin..text.clear[cc],0,-30,45,'spin')
yomi = yomi..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.block[C.name]..text.spin..text.clear[cc]
atk=2*cc
end
sendTime=20+atk*20
if mini then
self:showText(text.mini,0,-80,35,'appear')
yomi = text.mini..' '..yomi
yomi=text.mini..' '..yomi
atk=atk*.25
sendTime=sendTime+60
cscore=cscore*.5
@@ -1884,7 +1978,7 @@ do
cscore=clearSCR[cc]
if self.b2b>800 then
self:showText(text.b3b..text.clear[cc],0,-30,50,'fly')
yomi = text.b3b..text.clear[cc]..yomi
yomi=text.b3b..text.clear[cc]..yomi
atk=4*cc-10
sendTime=100
exblock=exblock+1
@@ -1895,7 +1989,7 @@ do
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.clear[cc],0,-30,50,'drive')
yomi = text.b2b..text.clear[cc]..yomi
yomi=text.b2b..text.clear[cc]..yomi
sendTime=80
atk=3*cc-7
cscore=cscore*1.3
@@ -1905,7 +1999,7 @@ do
end
else
self:showText(text.clear[cc],0,-30,70,'stretch')
yomi = text.clear[cc]..yomi
yomi=text.clear[cc]..yomi
sendTime=60
atk=2*cc-4
end
@@ -1913,7 +2007,7 @@ do
piece.special=true
else
self:showText(text.clear[cc],0,-30,35,'appear',(8-cc)*.3)
yomi = text.clear[cc]..yomi
yomi=text.clear[cc]..yomi
atk=cc-.5
sendTime=20+floor(atk*20)
cscore=cscore+clearSCR[cc]
@@ -1932,7 +2026,7 @@ do
atk=atk+1
end
self:showText(text.cmb[min(cmb,21)],0,25,15+min(cmb,15)*5,cmb<10 and 'appear' or 'flicker')
yomi = yomi..' '..text.cmb[min(cmb,21)]
yomi=yomi..' '..text.cmb[min(cmb,21)]
cscore=cscore+min(50*cmb,500)*(2*cc-1)
end
@@ -2460,6 +2554,28 @@ local function _updateFX(P,dt)
end
end
end
function Player:resolveIRS()
if self.bufferedIHS then
self:hold(true)
self.bufferedIHS=false
end
self.bufferedIRS=false
local pressing=self.keyPressing
if pressing[5] then
self:act_rot180()
elseif pressing[3] then
if pressing[4] then
self:act_rot180()
else
self:act_rotRight()
end
elseif pressing[4] then
self:act_rotLeft()
end
end
local function update_alive(P,dt)
local ENV=P.gameEnv
@@ -2518,6 +2634,18 @@ local function update_alive(P,dt)
end
end
-- Buffer IRS after IRS cut delay has elapsed.
-- The purpose of this is to allow the player to release their rotate key during the IRS cut delay,
-- which will allow them to avoid accidentally using IRS.
if P.bufferedDelay then
P.bufferedDelay=P.bufferedDelay-1
if P.bufferedDelay<=0 then
if P.bufferedIRS then
P:resolveIRS()
end
end
end
-- Moving pressed
if P.movDir~=0 then
local das,arr=ENV.das,ENV.arr
@@ -2877,7 +3005,7 @@ function Player:revive()
SFX.play('emit')
end
function Player:torikanEnd(requiredTime)
if self.stat.time < requiredTime then
if self.stat.time<requiredTime then
return false
end
self:_die()

View File

@@ -89,21 +89,24 @@ scene.widgetList={
WIDGET.newText{name='title', x=100, y=50,lim=626,font=70,align='L'},
WIDGET.newText{name='preview', x=520, y=610,font=40,align='R'},
WIDGET.newSlider{name='das', x=250, y=190,lim=230,w=600,axis={0,20,1},disp=SETval('das'), show=_sliderShow,code=SETsto('das')},
WIDGET.newSlider{name='arr', x=250, y=260,lim=230,w=525,axis={0,15,1},disp=SETval('arr'), show=_sliderShow,code=SETsto('arr')},
WIDGET.newSlider{name='sddas', x=250, y=330,lim=230,w=350,axis={0,10,1},disp=SETval('sddas'), show=_sliderShow,code=SETsto('sddas')},
WIDGET.newSlider{name='sdarr', x=250, y=400,lim=230,w=140,axis={0,4,1}, disp=SETval('sdarr'), show=_sliderShow,code=SETsto('sdarr')},
WIDGET.newSlider{name='dascut', x=250, y=470,lim=230,w=600,axis={0,20,1},disp=SETval('dascut'), show=_sliderShow,code=SETsto('dascut')},
WIDGET.newSlider{name='das', x=250, y=180,lim=230,w=600,axis={0,20,1},disp=SETval('das'), show=_sliderShow,code=SETsto('das')},
WIDGET.newSlider{name='arr', x=250, y=240,lim=230,w=525,axis={0,15,1},disp=SETval('arr'), show=_sliderShow,code=SETsto('arr')},
WIDGET.newSlider{name='sddas', x=250, y=300,lim=230,w=350,axis={0,10,1},disp=SETval('sddas'), show=_sliderShow,code=SETsto('sddas')},
WIDGET.newSlider{name='sdarr', x=250, y=360,lim=230,w=140,axis={0,4,1}, disp=SETval('sdarr'), show=_sliderShow,code=SETsto('sdarr')},
WIDGET.newSlider{name='dascut', x=250, y=420,lim=230,w=600,axis={0,20,1},disp=SETval('dascut'), show=_sliderShow,code=SETsto('dascut')},
WIDGET.newSlider{name='irscut', x=250, y=480,lim=230,w=600,axis={0,20,1},disp=SETval('irscut'), show=_sliderShow,code=SETsto('irscut')},
WIDGET.newSlider{name='dropcut',x=250, y=540,lim=230,w=300,axis={0,10,1},disp=SETval('dropcut'),show=_sliderShow,code=SETsto('dropcut')},
WIDGET.newSwitch{name='ihs', x=1100, y=260,lim=300, disp=SETval('ihs'), code=SETrev('ihs')},
WIDGET.newSwitch{name='irs', x=1100, y=330,lim=300, disp=SETval('irs'), code=SETrev('irs')},
WIDGET.newSwitch{name='ims', x=1100, y=400,lim=300, disp=SETval('ims'), code=SETrev('ims')},
WIDGET.newSwitch{name='ihs', x=1100, y=240,lim=300, disp=SETval('ihs'), code=SETrev('ihs')},
WIDGET.newSwitch{name='irs', x=1100, y=300,lim=300, disp=SETval('irs'), code=SETrev('irs')},
WIDGET.newSwitch{name='ims', x=1100, y=360,lim=300, disp=SETval('ims'), code=SETrev('ims')},
WIDGET.newButton{name='reset', x=160, y=640,w=200,h=100,color='lR',font=40,
code=function()
local _=SETTING
_.das,_.arr,_.dascut=10,2,0
_.sddas,_.sdarr=0,2
_.ihs,_.irs,_.ims=false,false,false
_.ihs,_.irs,_.ims,_.irscut=false,false,false,6
end},
WIDGET.newButton{name='back', x=1140, y=640,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene},
}