-------------------------------------------------
--Var P in other files represent Player object!--
-------------------------------------------------
local Player={}--Player class
local int,ceil,rnd=math.floor,math.ceil,math.random
local max,min,modf=math.max,math.min,math.modf
local ins,rem=table.insert,table.remove
local resume,yield,status=coroutine.resume,coroutine.yield,coroutine.status
local SFX,BGM,VOC,VIB,SYSFX=SFX,BGM,VOC,VIB,SYSFX
local FREEROW,TABLE,TEXT,NET,TASK=FREEROW,TABLE,TEXT,NET,TASK
local PLAYERS,PLY_ALIVE,GAME=PLAYERS,PLY_ALIVE,GAME
local ply_draw=require"parts.player.draw"
local ply_update=require"parts.player.update"
----------------------------------------------------
function Player:showText(text,dx,dy,font,style,spd,stop)
if self.gameEnv.text then
ins(self.bonus,TEXT.getText(text,150+dx,300+dy,font,style,spd,stop))
end
end
function Player:showTextF(text,dx,dy,font,style,spd,stop)
ins(self.bonus,TEXT.getText(text,150+dx,300+dy,font,style,spd,stop))
end
function Player:popScore(score,x,y)
self:showText(
score,x,y,
40-600/(score+20),
'score',2
)
end
function Player:createLockFX()
local CB=self.cur.bk
local t=12-self.gameEnv.lockFX*2
for i=1,#CB do
local y=self.curY+i-1
local L=self.clearedRow
for j=1,#L do
if L[j]==y then goto CONTINUE_skip end
end
y=-30*y
for j=1,#CB[1]do
if CB[i][j]then
ins(self.lockFX,{30*(self.curX+j-2),y,0,t})
end
end
::CONTINUE_skip::
end
end
function Player:createDropFX(x,y,w,h)
ins(self.dropFX,{x,y,w,h,0,13-2*self.gameEnv.dropFX})
end
function Player:createMoveFX(dir)
local T=10-1.5*self.gameEnv.moveFX
local C=self.cur.color
local CB=self.cur.bk
local x=self.curX-1
local y=self.gameEnv.smooth and self.curY+self.dropDelay/self.gameEnv.drop-2 or self.curY-1
if dir=='left'then
for i=1,#CB do for j=#CB[1],1,-1 do
if self.cur.bk[i][j]then
ins(self.moveFX,{C,x+j,y+i,0,T})
break
end
end end
elseif dir=='right'then
for i=1,#CB do for j=1,#CB[1]do
if self.cur.bk[i][j]then
ins(self.moveFX,{C,x+j,y+i,0,T})
break
end
end end
elseif dir=='down'then
for j=1,#CB[1]do for i=#CB,1,-1 do
if self.cur.bk[i][j]then
ins(self.moveFX,{C,x+j,y+i,0,T})
break
end
end end
else
for i=1,#CB do for j=1,#CB[1]do
if self.cur.bk[i][j]then
ins(self.moveFX,{C,x+j,y+i,0,T})
end
end end
end
end
function Player:createSplashFX(h)
local L=self.field[h]
local size=self.size
local y=self.fieldY+size*(self.fieldOff.y+self.fieldBeneath+self.fieldUp+615)
for x=1,10 do
local c=L[x]
if c>0 then
SYSFX.newCell(
2.5-self.gameEnv.splashFX*.4,
self.skinLib[c],
size,
self.fieldX+(30*x-15)*size,y-30*h*size,
rnd()*5-2.5,rnd()*-1,
0,.6
)
end
end
end
function Player:createClearingFX(y,spd)
ins(self.clearFX,{y,0,spd})
end
function Player:createBeam(R,send,power,color)
local x1,y1,x2,y2
if self.miniMode then x1,y1=self.centerX,self.centerY
else x1,y1=self.x+(30*(self.curX+self.cur.sc[2])-30+15+150)*self.size,self.y+(600-30*(self.curY+self.cur.sc[1])+15)*self.size
end
if R.small then x2,y2=R.centerX,R.centerY
else x2,y2=R.x+308*R.size,R.y+450*R.size
end
local c=minoColor[color]
local r,g,b=c[1]*2,c[2]*2,c[3]*2
local a=GAME.modeEnv.royaleMode and not(self.type=='human'or R.type=='human')and .2 or 1
SYSFX.newAttack(1-power*.1,x1,y1,x2,y2,int(send^.7*(4+power)),r,g,b,a*(power+2)*.0626)
end
----------------------------------------------------
----------------------------------------------------
function Player:newTask(code,...)
local thread=coroutine.create(code)
resume(thread,self,...)
if status(thread)~='dead'then
ins(self.tasks,{
thread=thread,
code=code,
args={...},
})
end
end
function Player:setPosition(x,y,size)
size=size or 1
self.x,self.y,self.size=x,y,size
if self.miniMode or self.demo then
self.fieldX,self.fieldY=x,y
self.centerX,self.centerY=x+300*size,y+600*size
else
self.fieldX,self.fieldY=x+150*size,y
self.centerX,self.centerY=x+300*size,y+370*size
self.absFieldX,self.absFieldY=x+150*size,y-10*size
end
end
do--function Player:movePosition(x,y,size)
local function task_movePosition(self,x,y,size)
local x1,y1,size1=self.x,self.y,self.size
while true do
yield()
if (x1-x)^2+(y1-y)^2<1 then
self:setPosition(x,y,size)
return true
else
x1=x1+(x-x1)*.126
y1=y1+(y-y1)*.126
size1=size1+(size-size1)*.126
self:setPosition(x1,y1,size1)
end
end
end
local function checkPlayer(obj,Ptar)
return obj.args[1]==Ptar
end
function Player:movePosition(x,y,size)
TASK.removeTask_iterate(checkPlayer,self)
TASK.new(task_movePosition,self,x,y,size or self.size)
end
end
local frameColorList={[0]=COLOR.Z,COLOR.lG,COLOR.lB,COLOR.lV,COLOR.lO}
function Player:setFrameColor(c)
self.frameColor=frameColorList[c]
end
function Player:switchKey(id,on)
self.keyAvailable[id]=on
if not on then
self:releaseKey(id)
end
if self.type=='human'then
VK.switchKey(id,on)
end
end
function Player:set20G(if20g)
self._20G=if20g
self:switchKey(13,not if20g)
self:switchKey(14,not if20g)
self:switchKey(15,not if20g)
self:switchKey(16,not if20g)
if if20g and self.AI_mode=='CC'then CC.switch20G(self)end
end
function Player:setHold(count)--Set hold count (false/true as 0/1)
if not count then
count=0
elseif count==true then
count=1
end
self:switchKey(8,count>0)
self.gameEnv.holdCount=count
self.holdTime=count
while self.holdQueue[count+1]do rem(self.holdQueue)end
end
function Player:setNext(next,hidden)--Set next count (use hidden=true if set env.nextStartPos>1)
self.gameEnv.nextCount=next
if next==0 then
self.drawNext=NULL
elseif not hidden then
self.drawNext=ply_draw.drawNext_norm
else
self.drawNext=ply_draw.drawNext_hidden
end
end
function Player:setInvisible(time)--Time in frames
if time<0 then
self.keepVisible=true
self.showTime=1e99
else
self.keepVisible=false
self.showTime=time
end
end
function Player:setRS(RSname)
self.RS=RSlist[RSname]or RSlist.TRS
end
function Player:getHolePos()--Get a good garbage-line hole position
if self.garbageBeneath==0 then
return generateLine(self.holeRND:random(10))
else
local p=self.holeRND:random(10)
if self.field[1][p]<=0 then
return generateLine(self.holeRND:random(10))
end
return generateLine(p)
end
end
function Player:garbageRelease()--Check garbage buffer and try to release them
local n,flag=1
while true do
local A=self.atkBuffer[n]
if A and A.countdown<=0 and not A.sent then
self:garbageRise(19+A.lv,A.amount,A.line)
self.atkBufferSum=self.atkBufferSum-A.amount
A.sent,A.time=true,0
self.stat.pend=self.stat.pend+A.amount
n=n+1
flag=true
else
break
end
end
if flag and self.AI_mode=='CC'and self.AI_bot then CC.updateField(self)end
end
function Player:garbageRise(color,amount,line)--Release n-lines garbage to field
local _
local t=self.showTime*2
for _=1,amount do
ins(self.field,1,FREEROW.get(0,true))
ins(self.visTime,1,FREEROW.get(t))
for i=1,10 do
self.field[1][i]=bit.rshift(line,i-1)%2==1 and color or 0
end
end
self.fieldBeneath=self.fieldBeneath+amount*30
if self.cur then
self.curY=self.curY+amount
self.ghoY=self.ghoY+amount
end
self.garbageBeneath=self.garbageBeneath+amount
for i=1,#self.clearingRow do
self.clearingRow[i]=self.clearingRow[i]+amount
end
self:freshBlock('push')
for i=1,#self.lockFX do
_=self.lockFX[i]
_[2]=_[2]-30*amount--Shift 30px per line cleared
end
for i=1,#self.dropFX do
_=self.dropFX[i]
_[3],_[5]=_[3]+amount,_[5]+amount
end
if #self.field>self.gameEnv.heightLimit then self:lose()end
end
local invList={2,1,4,3,5,6,7}
function Player:pushLineList(L,mir)--Push some lines to field
local l=#L
local S=self.gameEnv.skin
for i=1,l do
local r=FREEROW.get(0)
if not mir then
for j=1,10 do
r[j]=S[L[i][j]]or 0
end
else
for j=1,10 do
r[j]=S[invList[L[i][11-j]]]or 0
end
end
ins(self.field,1,r)
ins(self.visTime,1,FREEROW.get(20))
end
self.fieldBeneath=self.fieldBeneath+30*l
self.curY=self.curY+l
self.ghoY=self.ghoY+l
self:freshBlock('push')
end
function Player:pushNextList(L,mir)--Push some nexts to nextQueue
for i=1,#L do
self:getNext(mir and invList[L[i]]or L[i])
end
end
function Player:getCenterX()
return self.curX+self.cur.sc[2]-5.5
end
function Player:getCenterY()
return self.curY-self.cur.sc[1]
end
function Player:solid(x,y)
if x<1 or x>10 or y<1 then return true end
if y>#self.field then return false end
return self.field[y]
[x]>0--to catch bug (nil[*])
end
function Player:ifoverlap(bk,x,y)
local C=#bk[1]
if x<1 or x+C>11 or y<1 then return true end
if y>#self.field then return end
for i=1,#bk do
if self.field[y+i-1]then
for j=1,C do
if bk[i][j]and self.field[y+i-1][x+j-1]>0 then return true end
end
end
end
end
function Player:attack(R,send,time,line,fromStream)
local atkFX=self.gameEnv.atkFX
if GAME.net then
if self.type=='human'then--Local player attack others
ins(GAME.rep,self.frameRun)
ins(GAME.rep,
R.sid+
send*0x100+
time*0x10000+
line*0x100000000+
0x2000000000000
)
if atkFX then
self:createBeam(R,send,atkFX,self.cur.color)
end
end
if fromStream and R.type=='human'then--Local player receiving lines
ins(GAME.rep,R.frameRun)
ins(GAME.rep,
self.sid+
send*0x100+
time*0x10000+
line*0x100000000+
0x1000000000000
)
R:receive(self,send,time,line)
end
else
R:receive(self,send,time,line)
if atkFX then
self:createBeam(R,send,atkFX,self.cur.color)
end
end
end
function Player:receive(A,send,time,line)
self.lastRecv=A
local B=self.atkBuffer
if send+self.atkBufferSum>self.gameEnv.bufferLimit then send=self.gameEnv.bufferLimit-self.atkBufferSum end
if send>0 then
local m,k=#B,1
while k<=m and time>B[k].countdown do k=k+1 end
ins(B,k,{
line=line,
amount=send,
countdown=time,
cd0=time,
time=0,
sent=false,
lv=min(int(send^.69),5),
})--Sorted insert(by time)
self.atkBufferSum=self.atkBufferSum+send
self.stat.recv=self.stat.recv+send
if self.sound then
SFX.play(send<4 and'blip_1'or'blip_2',min(send+1,5)*.1)
end
end
end
function Player:clearAttackBuffer()
for i=1,#self.atkBuffer do
local A=self.atkBuffer[i]
if not A.sent then
A.sent=true
A.time=0
end
end
self.atkBufferSum=0
end
function Player:freshTarget()
if self.atkMode==1 then
if not self.atking or not self.atking.alive or rnd()<.1 then
self:changeAtk(randomTarget(self))
end
elseif self.atkMode==2 then
self:changeAtk(self~=GAME.mostBadge and GAME.mostBadge or GAME.secBadge or randomTarget(self))
elseif self.atkMode==3 then
self:changeAtk(self~=GAME.mostDangerous and GAME.mostDangerous or GAME.secDangerous or randomTarget(self))
elseif self.atkMode==4 then
for i=1,#self.atker do
if not self.atker[i].alive then
rem(self.atker,i)
return
end
end
end
end
function Player:changeAtkMode(m)
if self.atkMode==m then return end
self.atkMode=m
if m==1 then
self:changeAtk(randomTarget(self))
elseif m==2 or m==3 then
self:freshTarget()
elseif m==4 then
self:changeAtk()
end
end
function Player:changeAtk(R)
-- if self.type~='human'then R=PLAYERS[1]end--1vALL mode?
if self.atking then
local K=self.atking.atker
local i=TABLE.find(K,self)
if i then rem(K,i)end
end
if R then
self.atking=R
ins(R.atker,self)
else
self.atking=false
end
end
function Player:freshBlock(mode)--string mode: push/move/fresh/newBlock
local ENV=self.gameEnv
--Fresh ghost
if(mode=='move'or mode=='newBlock'or mode=='push')and self.cur then
local CB=self.cur.bk
self.ghoY=min(#self.field+1,self.curY)
if self._20G or ENV.sdarr==0 and self.keyPressing[7]and self.downing>ENV.sddas then
local _=self.ghoY
--Move ghost to bottom
while not self:ifoverlap(CB,self.curX,self.ghoY-1)do
self.ghoY=self.ghoY-1
end
--Cancel spinLast
if _~=self.ghoY then
self.spinLast=false
end
--Create FX if dropped
if self.curY>self.ghoY then
if ENV.dropFX and ENV.block and self.curY-self.ghoY-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-self.ghoY-#CB+1)
end
if ENV.shakeFX then
self.fieldOff.vy=ENV.shakeFX*.5
end
self.curY=self.ghoY
end
else
while not self:ifoverlap(CB,self.curX,self.ghoY-1)do
self.ghoY=self.ghoY-1
end
end
end
--Fresh delays
if mode=='move'or mode=='newBlock'or mode=='fresh'then
local d0,l0=ENV.drop,ENV.lock
if ENV.easyFresh then
if self.lockDelay0 then
if mode~='newBlock'then
self.freshTime=self.freshTime-1
end
self.lockDelay=l0
self.dropDelay=d0
end
if self.curY+self.cur.sc[1]0 then
self.freshTime=self.freshTime-1
self.dropDelay=d0
self.lockDelay=l0
end
end
end
end
end
function Player:lock()
local dest=self.AI_dest
local has_dest=dest~=nil
local CB=self.cur.bk
for i=1,#CB do
local y=self.curY+i-1
if not self.field[y]then self.field[y],self.visTime[y]=FREEROW.get(0),FREEROW.get(0)end
for j=1,#CB[1]do
if CB[i][j]then
self.field[y][self.curX+j-1]=self.cur.color
self.visTime[y][self.curX+j-1]=self.showTime
if dest then
local x=self.curX+j-1
for k=1,#dest,2 do
if x==dest[k]+1 and y==dest[k+1]+1 then
rem(dest,k)rem(dest,k)
goto BREAK_success
end
end
dest=nil
::BREAK_success::
end
end
end
end
if has_dest and not dest and self.AI_mode=='CC'and self.AI_bot then
CC.updateField(self)
end
end
function Player:checkClear(field,start,height,CB,CX)
local cc=0
if self.gameEnv.fillClear then
for i=1,height do
local h=start+i-2
--Bomb trigger (optional, must with CB)
if CB and h>0 and field[h]and self.clearedRow[cc]~=h then
for x=1,#CB[1]do
if CB[i][x]and field[h][CX+x-1]==19 then
cc=cc+1
self.clearingRow[cc]=h-cc+1
self.clearedRow[cc]=h
break
end
end
end
h=h+1
--Row filled
for x=1,10 do
if field[h][x]<=0 then
goto CONTINUE_notFull
end
end
cc=cc+1
ins(self.clearingRow,h-cc+1)
ins(self.clearedRow,h)
::CONTINUE_notFull::
end
end
return cc
end
function Player:roofCheck()
local CB=self.cur.bk
for x=1,#CB[1]do
local y=#CB
--Find the highest y of blocks' x-th column
while not CB[y][x]do y=y-1 end
local testX=self.curX+x-1--Optimize
--Test the whole column of field to find roof
for testY=self.curY+y,#self.field do
if self:solid(testX,testY)then
return true
end
end
end
return false
end
function Player:removeTopClearingFX()
for i=#self.clearingRow,1,-1 do
if self.clearingRow[i]>#self.field then
rem(self.clearingRow)
else
return
end
end
end
function Player:checkMission(piece,mission)
if mission<5 then
return piece.row==mission and not piece.spin
elseif mission<9 then
return piece.row==mission-4 and piece.spin
elseif mission==9 then
return piece.pc
elseif mission<90 then
return piece.row==mission%10 and piece.name==int(mission/10)and piece.spin
end
return false
end
local spawnSFX_name={}for i=1,7 do spawnSFX_name[i]='spawn_'..i end
function Player:resetBlock()--Reset Block's position and execute I*S
local B=self.cur.bk
self.curX=int(6-#B[1]*.5)
local y=int(self.gameEnv.fieldH+1-modf(self.cur.sc[1]))+ceil(self.fieldBeneath/30)
self.curY=y
self.minY=y+self.cur.sc[1]
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(B,x,y)then
self.curX=x
end
end
--IRS
if self.gameEnv.irs then
if pressing[5]then
self:spin(2,true)
elseif pressing[3]then
if pressing[4]then
self:spin(2,true)
else
self:spin(1,true)
end
elseif pressing[4]then
self:spin(3,true)
end
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
end
--Spawn SFX
if self.sound and self.cur.id<8 then
SFX.fplay(spawnSFX_name[self.cur.id],SETTING.sfx_spawn)
end
end
function Player:spin(d,ifpre)
local cur=self.cur
local kickData=self.RS.kickTable[cur.id]
if type(kickData)=='table'then
local idir=(cur.dir+d)%4
kickData=kickData[cur.dir*10+idir]
if not kickData then
self:freshBlock('move')
SFX.play(ifpre and'prerotate'or'rotate',nil,self:getCenterX()*.15)
return
end
local icb=BLOCKS[cur.id][idir]
local isc=self.RS.centerPos[cur.id][idir]
local baseX,baseY=self.curX+cur.sc[2]-isc[2],self.curY+cur.sc[1]-isc[1]
for test=1,#kickData do
local ix,iy=baseX+kickData[test][1],baseY+kickData[test][2]
if (self.freshTime>0 or kickData[test][2]<=0)and not self:ifoverlap(icb,ix,iy)then
--Create moveFX at the original position
if self.gameEnv.moveFX and self.gameEnv.block then self:createMoveFX()end
--Change block position
cur.sc,cur.bk,cur.dir=isc,icb,idir
self.curX,self.curY=ix,iy
self.spinLast=test==2 and 0 or 1
--Fresh ghost and freshTime
local t=self.freshTime
if not ifpre then self:freshBlock('move')end
if kickData[test][2]>0 and self.freshTime==t and self.curY~=self.imgY then
self.freshTime=self.freshTime-1
end
--Sound & Field shaking
if self.sound then
local sfx
if ifpre then
sfx='prerotate'
elseif self:ifoverlap(icb,ix,iy+1)and self:ifoverlap(icb,ix-1,iy)and self:ifoverlap(icb,ix+1,iy)then
sfx='rotatekick'
if self.gameEnv.shakeFX then
if d==1 or d==3 then
self.fieldOff.va=self.fieldOff.va+(2-d)*self.gameEnv.shakeFX*6e-3
else
self.fieldOff.va=self.fieldOff.va+self:getCenterX()*self.gameEnv.shakeFX*3e-3
end
end
else
sfx='rotate'
end
SFX.play(sfx,nil,self:getCenterX()*.15)
end
self.stat.rotate=self.stat.rotate+1
return
end
end
elseif kickData then
kickData(self,d)
else
self:freshBlock('move')
SFX.play(ifpre and'prerotate'or'rotate',nil,self:getCenterX()*.15)
end
end
local phyHoldKickX={
[true]={0,-1,1},--X==?.0 tests
[false]={-.5,.5},--X==?.5 tests
}
function Player:hold(ifpre)
local ENV=self.gameEnv
if self.holdTime>0 and(ifpre or self.waiting==-1)then
if #self.holdQueue All test failed, interrupt with sound
SFX.play('finesseError')
do return end
--
::BREAK_success::
self.spinLast=false
self.spinSeq=0
local hb=self:getBlock(C.id)
hb.name=C.name
hb.color=C.color
ins(self.holdQueue,hb)
self.cur=rem(self.holdQueue,1)
self.curX,self.curY=x,y
else--Normal hold
self.spinLast=false
self.spinSeq=0
if C then
local hb=self:getBlock(C.id)
hb.color=C.color
hb.name=C.name
ins(self.holdQueue,hb)
end
self.cur=rem(self.holdQueue,1)
self:resetBlock()
end
self:freshBlock('move')
self.dropDelay=ENV.drop
self.lockDelay=ENV.lock
if self:ifoverlap(self.cur.bk,self.curX,self.curY)then
self:lock()
self:lose()
end
end
self.freshTime=int(min(self.freshTime+ENV.freshLimit*.25,ENV.freshLimit*((self.holdTime+1)/ENV.holdCount),ENV.freshLimit))
if not ENV.infHold then
self.holdTime=self.holdTime-1
end
if self.sound then
SFX.play(ifpre and'prehold'or'hold')
end
if self.AI_mode=='CC'then
local next=self.nextQueue[self.AIdata.nextCount]
if next then
CC.addNext(self.AI_bot,next.id)
end
end
self.stat.hold=self.stat.hold+1
end
end
function Player:getBlock(id,name,color)--Get a block(id=n) object
local E=self.gameEnv
local dir=E.face[id]
return{
id=id,
dir=dir,
bk=BLOCKS[id][dir],
sc=self.RS.centerPos[id][dir],
name=name or id,
color=E.bone and 17 or color or E.skin[id],
}
end
function Player:getNext(n)--Push a block(id=n) to nextQueue
local E=self.gameEnv
local dir=E.face[n]
ins(self.nextQueue,{
id=n,
bk=BLOCKS[n][dir],
sc=self.RS.centerPos[n][dir],
dir=dir,
name=n,
color=E.bone and 17 or E.skin[n],
})
end
function Player:popNext(ifhold)--Pop nextQueue to hand
if not ifhold then
self.holdTime=min(self.holdTime+1,self.gameEnv.holdCount)
end
self.spinLast=false
self.spinSeq=0
self.ctrlCount=0
self.cur=rem(self.nextQueue,1)
self.newNext()
if self.cur then
self.pieceCount=self.pieceCount+1
if self.AI_mode=='CC'then
local next=self.nextQueue[self.AIdata.next]
if next then
CC.addNext(self.AI_bot,next.id)
end
end
local pressing=self.keyPressing
--IHS
if not ifhold and pressing[8]and self.gameEnv.ihs and self.holdTime>0 then
self:hold(true)
pressing[8]=false
else
self:resetBlock()
end
self.dropDelay=self.gameEnv.drop
self.lockDelay=self.gameEnv.lock
self.freshTime=self.gameEnv.freshLimit
if self.cur then
if self:ifoverlap(self.cur.bk,self.curX,self.curY)then
self:lock()
self:lose()
end
self:freshBlock('newBlock')
end
--IHdS
if pressing[6]and not ifhold then
self:act_hardDrop()
pressing[6]=false
end
else
self:hold()
end
end
function Player:cancel(N)--Cancel Garbage
local off=0--Lines offseted
local bf=self.atkBuffer
for i=1,#bf do
if self.atkBufferSum==0 or N==0 then break end
local A=bf[i]
if not A.sent then
local O=min(A.amount,N)--Cur Offset
if N2 then dospin=dospin+2 end
end
end
end
--Immovable spin check
if self:ifoverlap(CB,CX,CY+1)and self:ifoverlap(CB,CX-1,CY)and self:ifoverlap(CB,CX+1,CY)then
dospin=dospin+2
end
--Lock block to field
self:lock()
--Clear list of cleared-rows
if self.clearedRow[1]then TABLE.cut(self.clearedRow)end
--Check line clear
cc=cc+self:checkClear(self.field,CY,#CB,CB,CX)
--Create clearing FX
for i=1,cc do
local y=self.clearedRow[i]
if ENV.clearFX then self:createClearingFX(y,7-ENV.clearFX)end
if ENV.splashFX then self:createSplashFX(y)end
end
--Create locking FX
if ENV.lockFX then
if cc==0 then
self:createLockFX()
else
_=#self.lockFX
if _>0 then
for _=1,_ do
rem(self.lockFX)
end
end
end
end
--Final spin check
if dospin>0 then
if cc>0 then
dospin=dospin+(self.spinLast or 0)
if dospin<3 then
mini=C.id<6 and cc<#CB
end
end
else
dospin=false
end
--Finesse: roof check
local finesse=CY>ENV.fieldH-2 or self:roofCheck()
--Remove rows need to be cleared
if cc>0 then
for i=cc,1,-1 do
_=self.clearedRow[i]
if self.field[_].garbage then
self.garbageBeneath=self.garbageBeneath-1
gbcc=gbcc+1
end
FREEROW.discard(rem(self.field,_))
FREEROW.discard(rem(self.visTime,_))
end
end
--Cancel top clearing FX
self:removeTopClearingFX()
if self.clearingRow[1]then
self.falling=ENV.fall
else
clear=true
end
--Finesse check (control)
local finePts
if not finesse then
if dospin then self.ctrlCount=self.ctrlCount-2 end--Allow 2 more step for roof-less spin
local id=C.id
local d=self.ctrlCount-finesseList[id][C.dir+1][CX]
finePts=d<=0 and 5 or max(3-d,0)
else
finePts=5
end
piece.finePts=finePts
Stat.finesseRate=Stat.finesseRate+finePts
if finePts<5 then
Stat.extraPiece=Stat.extraPiece+1
if ENV.fineKill then
finish=true
end
if self.sound then
if ENV.fineKill then
SFX.play('finesseError_long',.6)
elseif ENV.fine then
SFX.play('finesseError',.8)
else
SFX.play('lock',nil,self:getCenterX()*.15)
end
end
elseif self.sound then
SFX.play('lock',nil,self:getCenterX()*.15)
end
if finePts<=1 then
self.finesseCombo=0
else
self.finesseCombo=self.finesseCombo+1
if self.finesseCombo>2 then
self.finesseComboTime=12
end
end
piece.spin,piece.mini=dospin,false
piece.pc,piece.hpc=false,false
piece.special=false
if cc>0 then--If lines cleared,about 200 lines of codes below
cmb=cmb+1
if dospin then
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')
atk=b2bATK[cc]+cc*.5
exblock=exblock+1
cscore=cscore*2
Stat.b3b=Stat.b3b+1
if self.sound then
VOC.play('b3b',CHN)
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.block[C.name]..text.spin.." "..text.clear[cc],0,-30,35,'spin')
atk=b2bATK[cc]
cscore=cscore*1.2
Stat.b2b=Stat.b2b+1
if self.sound then
VOC.play('b2b',CHN)
end
else
self:showText(text.block[C.name]..text.spin.." "..text.clear[cc],0,-30,45,'spin')
atk=2*cc
end
sendTime=20+atk*20
if mini then
self:showText(text.mini,0,-80,35,'appear')
atk=atk*.25
sendTime=sendTime+60
cscore=cscore*.5
self.b2b=self.b2b+b2bPoint[cc]*.5
if self.sound then
VOC.play('mini',CHN)
end
else
self.b2b=self.b2b+b2bPoint[cc]
end
piece.mini=mini
piece.special=true
if self.sound then
SFX.play(spinSFX[cc]or'spin_3')
VOC.play(spinVoice[C.name],CHN)
end
elseif cc>=4 then
cscore=cc==4 and 1000 or cc==5 and 1500 or 2000
if self.b2b>800 then
self:showText(text.b3b..text.clear[cc],0,-30,50,'fly')
atk=4*cc-10
sendTime=100
exblock=exblock+1
cscore=cscore*1.8
Stat.b3b=Stat.b3b+1
if self.sound then
VOC.play('b3b',CHN)
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.clear[cc],0,-30,50,'drive')
sendTime=80
atk=3*cc-7
cscore=cscore*1.3
Stat.b2b=Stat.b2b+1
if self.sound then
VOC.play('b2b',CHN)
end
else
self:showText(text.clear[cc],0,-30,70,'stretch')
sendTime=60
atk=2*cc-4
end
self.b2b=self.b2b+cc*50-50
piece.special=true
else
piece.special=false
end
if self.sound then
VOC.play(clearVoice[cc],CHN)
end
--Normal clear,reduce B2B point
if not piece.special then
self.b2b=max(self.b2b-250,0)
self:showText(text.clear[cc],0,-30,35,'appear',(8-cc)*.3)
atk=cc-.5
sendTime=20+int(atk*20)
cscore=cscore+clearSCR[cc]
end
--Combo bonus
sendTime=sendTime+25*cmb
if cmb>1 then
atk=atk*(1+(cc==1 and .15 or .25)*min(cmb-1,12))
if cmb>=3 then
atk=atk+1
end
self:showText(text.cmb[min(cmb,21)],0,25,15+min(cmb,15)*5,cmb<10 and'appear'or'flicker')
cscore=cscore+min(50*cmb,500)*(2*cc-1)
end
--PC/HPC
if clear and cc>=#C.bk then
if CY==1 then
self:showText(text.PC,0,-80,50,'flicker')
atk=max(atk,min(8+Stat.pc*2,16))
exblock=exblock+2
sendTime=sendTime+120
if Stat.row+cc>4 then
self.b2b=self.b2b+800
cscore=cscore+300*min(6+Stat.pc,10)
else
cscore=cscore+626
end
Stat.pc=Stat.pc+1
if self.sound then
SFX.play('clear')
VOC.play('perfect_clear',CHN)
end
piece.pc=true
piece.special=true
elseif cc>1 or #self.field==self.garbageBeneath then
self:showText(text.HPC,0,-80,50,'fly')
atk=atk+4
exblock=exblock+2
sendTime=sendTime+60
self.b2b=self.b2b+100
cscore=cscore+626
Stat.hpc=Stat.hpc+1
if self.sound then
SFX.play('clear')
VOC.play('half_clear',CHN)
end
piece.hpc=true
piece.special=true
end
end
if self.b2b>1000 then
self.b2b=1000
elseif self.b2b==0 and ENV.b2bKill then
finish=true
end
--Bonus atk/def when focused
if GAME.modeEnv.royaleMode then
local i=min(#self.atker,9)
if i>1 then
atk=atk+reAtk[i]
exblock=exblock+reDef[i]
end
end
--Send Lines
atk=int(atk*(1+self.strength*.25))--Badge Buff
send=atk
if exblock>0 then
exblock=int(exblock*(1+self.strength*.25))--Badge Buff
self:showText("+"..exblock,0,53,20,'fly')
off=off+self:cancel(exblock)
end
if send>=1 then
self:showText(send,0,80,35,'zoomout')
_=self:cancel(send)
send=send-_
off=off+_
if send>0 then
local T
if GAME.modeEnv.royaleMode then
if self.atkMode==4 then
local M=#self.atker
if M>0 then
for i=1,M do
self:attack(self.atker[i],send,sendTime,generateLine(self.atkRND:random(10)))
end
else
T=randomTarget(self)
end
else
T=self.atking
self:freshTarget()
end
elseif #PLY_ALIVE>1 then
T=randomTarget(self)
end
if T then
self:attack(T,send,sendTime,generateLine(self.atkRND:random(10)))
end
end
if self.sound and send>3 then SFX.play('emit',min(send,7)*.1)end
end
--SFX & Vibrate
if self.sound then
SFX.play(clearSFX[cc]or'clear_4')
SFX.play(renSFX[min(cmb,11)])
if cmb>14 then SFX.play('ren_mega',(cmb-10)*.1)end
VIB(cc+1)
end
else--No lines clear
cmb=0
--Spin bonus
if dospin then
self:showText(text.block[C.name]..text.spin,0,-30,45,'appear')
self.b2b=self.b2b+20
if self.sound then
SFX.play('spin_0')
VOC.play(spinVoice[C.name],CHN)
end
cscore=30
end
if self.b2b>800 then
self.b2b=max(self.b2b-40,800)
end
self:garbageRelease()
end
self.combo=cmb
--Spike
if atk>0 then
self.spike=self.spikeTime==0 and atk or self.spike+atk
self.spikeTime=min(self.spikeTime+atk*20,100)
self.spikeText:set(self.spike)
end
--DropSpeed bonus
if self._20G then
cscore=cscore*2
elseif ENV.drop<1 then
cscore=cscore*1.5
elseif ENV.drop<3 then
cscore=cscore*1.2
end
--Speed bonus
if self.dropSpeed>60 then cscore=cscore*(.9+self.dropSpeed/600)end
cscore=int(cscore)
if ENV.score then
self:popScore(
cscore,
self:getCenterX()*30,
(10-self:getCenterY())*30+self.fieldBeneath+self.fieldUp
)
end
piece.row,piece.dig=cc,gbcc
piece.score=cscore
piece.atk,piece.exblock=atk,exblock
piece.off,piece.send=off,send
--Check clearing task
if cc>0 and self.curMission then
if self:checkMission(piece,ENV.mission[self.curMission])then
self.curMission=self.curMission+1
SFX.play('reach')
if self.curMission>#ENV.mission then
self.curMission=false
if not finish then finish='finish'end
end
elseif ENV.missionKill then
self:showText(text.missionFailed,0,140,40,'flicker',.5)
SFX.play('finesseError_long',.6)
finish=true
end
end
--Check height limit
if cc==0 and #self.field>ENV.heightLimit then self:lose()end
--Update stat
Stat.piece=Stat.piece+1
Stat.row=Stat.row+cc
Stat.maxFinesseCombo=max(Stat.maxFinesseCombo,self.finesseCombo)
Stat.maxCombo=max(Stat.maxCombo,self.combo)
Stat.score=Stat.score+cscore
if atk>0 then
Stat.atk=Stat.atk+atk
if send>0 then
Stat.send=Stat.send+int(send)
end
if off>0 then
Stat.off=Stat.off+off
end
end
if gbcc>0 then
Stat.dig=Stat.dig+gbcc
if atk>0 then
Stat.digatk=Stat.digatk+atk*gbcc/cc
end
end
local n=C.name
if dospin then
_=Stat.spin[n] _[cc+1]=_[cc+1]+1--Spin[1~25][0~4]
_=Stat.spins _[cc+1]=_[cc+1]+1--Spin[0~4]
elseif cc>0 then
_=Stat.clear[n] _[cc]=_[cc]+1--Clear[1~25][1~5]
_=Stat.clears _[cc]=_[cc]+1--Clear[1~5]
end
if finish then
if finish==true then self:lose()return end
_=ENV.dropPiece if _ then _(self)end
if finish then self:win(finish)end
else
_=ENV.dropPiece if _ then _(self)end
end
end
end
function Player:loadAI(data)--Load AI params
if not CC then
data.type='9S'
data.delta=int(data.delta*.3)
end
self.AI_mode=data.type
self.AI_keys={}
self.AI_delay=min(int(self.gameEnv.drop*.8),data.delta*rnd()*4)
self.AI_delay0=data.delta
self.AIdata={
type=data.type,
delay=data.delay,
delta=data.delta,
next=data.next,
hold=data.hold,
_20G=self._20G,
bag=data.bag,
node=data.node,
}
if self.AI_mode=='CC'then
self:setRS('SRS')
local opt,wei=CC.getConf()
CC.fastWeights(wei)
CC.setHold(opt,self.AIdata.hold)
CC.set20G(opt,self.AIdata._20G)
CC.setBag(opt,self.AIdata.bag=='bag')
CC.setNode(opt,self.AIdata.node)
self.AI_bot=CC.new(opt,wei)
CC.free(opt)CC.free(wei)
for i=1,self.AIdata.next do
CC.addNext(self.AI_bot,self.nextQueue[i].id)
end
if self.gameEnv.holdCount and self.gameEnv.holdCount>1 then
self:setHold(1)
end
else
self:setRS('TRS')
end
self.AI_thread=coroutine.wrap(AIFUNC[data.type])
self:AI_thread(self.AI_keys)
end
----------------------------------------------------
----------------------------------------------------
local function tick_throwBadge(ifAI,sender,time)
while true do
yield()
time=time-1
if time%4==0 then
local S,R=sender,sender.lastRecv
local x1,y1,x2,y2
if S.small then
x1,y1=S.centerX,S.centerY
else
x1,y1=S.x+308*S.size,S.y+450*S.size
end
if R.small then
x2,y2=R.centerX,R.centerY
else
x2,y2=R.x+66*R.size,R.y+344*R.size
end
--Generate badge object
SYSFX.newBadge(x1,y1,x2,y2)
if not ifAI and time%8==0 then
SFX.play('collect')
end
end
if time<=0 then return end
end
end
local function tick_finish(self)
while true do
yield()
self.endCounter=self.endCounter+1
if self.endCounter<40 then
--Make field visible
for j=1,#self.field do for i=1,10 do
if self.visTime[j][i]<20 then self.visTime[j][i]=self.visTime[j][i]+.5 end
end end
elseif self.endCounter==60 then
return
end
end
end
local function tick_lose(self)
while true do
yield()
self.endCounter=self.endCounter+1
if self.endCounter<40 then
--Make field visible
for j=1,#self.field do for i=1,10 do
if self.visTime[j][i]<20 then self.visTime[j][i]=self.visTime[j][i]+.5 end
end end
elseif self.endCounter>80 then
for i=1,#self.field do
for j=1,10 do
if self.visTime[i][j]>0 then
self.visTime[i][j]=self.visTime[i][j]-1
end
end
end
if self.endCounter==120 then
for _=#self.field,1,-1 do
FREEROW.discard(self.field[_])
FREEROW.discard(self.visTime[_])
self.field[_],self.visTime[_]=nil
end
return
end
end
if not GAME.modeEnv.royaleMode and #PLAYERS>1 then
self.y=self.y+self.endCounter*.26
self.absFieldY=self.absFieldY+self.endCounter*.26
end
end
end
local function tick_autoPause()
local time=0
while true do
yield()
time=time+1
if SCN.cur~='game'or PLAYERS[1].frameRun<180 then
return
elseif time==120 then
pauseGame()
return
end
end
end
----------------------------------------------------
----------------------------------------------------
function Player:die()--Called both when win/lose!
self.alive=false
self.timing=false
self.control=false
self.update=ply_update.dead
self.waiting=1e99
self.b2b=0
self.tasks={}
self:clearAttackBuffer()
for i=1,#self.field do
for j=1,10 do
self.visTime[i][j]=min(self.visTime[i][j],20)
end
end
if GAME.net then
if self.id==1 then
ins(GAME.rep,self.frameRun)
ins(GAME.rep,0)
else
if self.lastRecv and self.lastRecv.id==1 then
SFX.play('collect')
end
end
end
end
function Player:revive()
self.waiting=62
local h=#self.field
for _=h,1,-1 do
FREEROW.discard(self.field[_])
FREEROW.discard(self.visTime[_])
self.field[_],self.visTime[_]=nil
end
self.garbageBeneath=0
if self.AI_mode=='CC'then
CC.destroy(self.AI_bot)
TABLE.cut(self.holdQueue)
self:loadAI(self.AIdata)
end
self:clearAttackBuffer()
self.life=self.life-1
self.fieldBeneath=0
self.b2b=0
for i=1,h do
self:createClearingFX(i,1.5)
end
SYSFX.newShade(1.4,self.fieldX,self.fieldY,300*self.size,610*self.size)
SYSFX.newRectRipple(2,self.fieldX,self.fieldY,300*self.size,610*self.size)
SYSFX.newRipple(2,self.x+(475+25*(self.life<3 and self.life or 0)+12)*self.size,self.y+(665+12)*self.size,20)
SFX.play('clear_3')
SFX.play('emit')
end
function Player:win(result)
if self.result then return end
self:die()
self.result='win'
if GAME.modeEnv.royaleMode then
self.modeData.place=1
self:changeAtk()
end
if self.type=='human'then
GAME.result=result or'gamewin'
SFX.play('win')
VOC.play('win')
if GAME.modeEnv.royaleMode then
BGM.play('8-bit happiness')
end
end
if GAME.curMode.name=='custom_puzzle'then
self:showTextF(text.win,0,0,90,'beat',.4)
else
self:showTextF(text.win,0,0,90,'beat',.5,.2)
end
if self.type=='human'then
gameOver()
TASK.new(tick_autoPause)
end
self:newTask(tick_finish)
end
function Player:lose(force)
if self.result then return end
if not force then
if self.life>0 then self:revive()return end
if self.type=='remote'then self.waiting=1e99 return end
end
self:die()
local p=TABLE.find(PLY_ALIVE,self)if p then rem(PLY_ALIVE,p)end
self.result='lose'
if GAME.modeEnv.royaleMode then
self:changeAtk()
self.modeData.place=#PLY_ALIVE+1
self.strength=0
if self.lastRecv then
local A,depth=self,0
repeat
A,depth=A.lastRecv,depth+1
until not A or A.alive or A==self or depth==3
if A and A.alive then
if self.id==1 or A.id==1 then
self.killMark=A.id==1
end
A.modeData.ko,A.badge=A.modeData.ko+1,A.badge+self.badge+1
for i=A.strength+1,4 do
if A.badge>=ROYALEDATA.powerUp[i]then
A.strength=i
A:setFrameColor(i)
end
end
self.lastRecv=A
if self.id==1 or A.id==1 then
TASK.new(tick_throwBadge,not A.type=='human',self,max(3,self.badge)*4)
end
end
else
self.badge=-1
end
freshMostBadge()
freshMostDangerous()
if #PLY_ALIVE==ROYALEDATA.stage[GAME.stage]then
royaleLevelup()
end
self:showTextF(self.modeData.place,0,120,60,'appear',.26,.9)
end
self.gameEnv.keepVisible=self.gameEnv.visible~='show'
self:showTextF(text.lose,0,0,90,'appear',.26,.9)
if self.type=='human'then
GAME.result='gameover'
SFX.play('fail')
VOC.play('lose')
if GAME.modeEnv.royaleMode then
BGM.play('end')
end
gameOver()
self:newTask(#PLAYERS>1 and tick_lose or tick_finish)
if GAME.net and not NET.spectate then
NET.signal_die()
else
TASK.new(tick_autoPause)
end
else
self:newTask(tick_lose)
end
if #PLY_ALIVE==1 then
PLY_ALIVE[1]:win()
end
end
--------------------------<\Events>--------------------------
----------------------------------------------------
function Player:act_moveLeft(auto)
if not auto then
self.ctrlCount=self.ctrlCount+1
end
self.movDir=-1
if self.keyPressing[9]then
if self.gameEnv.swap then
self:changeAtkMode(1)
self.keyPressing[1]=false
end
elseif self.control and self.waiting==-1 then
if self.cur and not self:ifoverlap(self.cur.bk,self.curX-1,self.curY)then
if self.gameEnv.moveFX and self.gameEnv.block then
self:createMoveFX('left')
end
self.curX=self.curX-1
self:freshBlock('move')
if self.sound and self.curY==self.ghoY then SFX.play('move')end
if not auto then self.moving=0 end
self.spinLast=false
else
self.moving=self.gameEnv.das
end
else
self.moving=0
end
end
function Player:act_moveRight(auto)
if not auto then
self.ctrlCount=self.ctrlCount+1
end
self.movDir=1
if self.keyPressing[9]then
if self.gameEnv.swap then
self:changeAtkMode(2)
self.keyPressing[2]=false
end
elseif self.control and self.waiting==-1 then
if self.cur and not self:ifoverlap(self.cur.bk,self.curX+1,self.curY)then
if self.gameEnv.moveFX and self.gameEnv.block then
self:createMoveFX('right')
end
self.curX=self.curX+1
self:freshBlock('move')
if self.sound and self.curY==self.ghoY then SFX.play('move')end
if not auto then self.moving=0 end
self.spinLast=false
else
self.moving=self.gameEnv.das
end
else
self.moving=0
end
end
function Player:act_rotRight()
if self.control and self.waiting==-1 and self.cur then
self.ctrlCount=self.ctrlCount+1
self:spin(1)
self.keyPressing[3]=false
end
end
function Player:act_rotLeft()
if self.control and self.waiting==-1 and self.cur then
self.ctrlCount=self.ctrlCount+1
self:spin(3)
self.keyPressing[4]=false
end
end
function Player:act_rot180()
if self.control and self.waiting==-1 and self.cur then
self.ctrlCount=self.ctrlCount+2
self:spin(2)
self.keyPressing[5]=false
end
end
function Player:act_hardDrop()
local ENV=self.gameEnv
if self.keyPressing[9]then
if ENV.swap then
self:changeAtkMode(3)
end
self.keyPressing[6]=false
elseif self.control and self.waiting==-1 and self.cur then
if self.lastPiece.autoLock and self.frameRun-self.lastPiece.frameself.ghoY then
local CB=self.cur.bk
if ENV.dropFX and ENV.block and self.curY-self.ghoY-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-self.ghoY-#CB+1)
end
self.curY=self.ghoY
self.spinLast=false
if self.sound then
SFX.play('drop',nil,self:getCenterX()*.15)
VIB(1)
end
end
if ENV.shakeFX then
self.fieldOff.vy=ENV.shakeFX*.6
self.fieldOff.va=self.fieldOff.va+self:getCenterX()*ENV.shakeFX*6e-4
end
self.lockDelay=-1
self:drop()
self.keyPressing[6]=false
end
end
end
function Player:act_softDrop()
local ENV=self.gameEnv
if self.keyPressing[9]then
if ENV.swap then
self:changeAtkMode(4)
end
else
self.downing=1
if self.control and self.waiting==-1 and self.cur then
if self.curY>self.ghoY then
self.curY=self.curY-1
self:freshBlock('fresh')
self.spinLast=false
elseif ENV.deepDrop then
local CB=self.cur.bk
local y=self.curY-1
while self:ifoverlap(CB,self.curX,y)and y>0 do
y=y-1
end
if y>0 then
if ENV.dropFX and ENV.block and self.curY-y-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-y-#CB+1)
end
self.curY=y
self:freshBlock('move')
SFX.play('swipe')
end
end
end
end
end
function Player:act_hold()
if self.control then
if self.waiting==-1 then
self:hold()
self.keyPressing[8]=false
end
end
end
function Player:act_func1()
self.gameEnv.fkey1(self)
end
function Player:act_func2()
self.gameEnv.fkey2(self)
end
function Player:act_insLeft(auto)
if not self.cur then return end
local x0=self.curX
while not self:ifoverlap(self.cur.bk,self.curX-1,self.curY)do
if self.gameEnv.moveFX and self.gameEnv.block then
self:createMoveFX('left')
end
self.curX=self.curX-1
self:freshBlock('move')
end
if self.curX~=x0 then
self.spinLast=false
end
if self.gameEnv.shakeFX then
self.fieldOff.vx=-self.gameEnv.shakeFX*.5
end
if auto then
if self.ctrlCount==0 then self.ctrlCount=1 end
else
self.ctrlCount=self.ctrlCount+1
end
end
function Player:act_insRight(auto)
if not self.cur then return end
local x0=self.curX
while not self:ifoverlap(self.cur.bk,self.curX+1,self.curY)do
if self.gameEnv.moveFX and self.gameEnv.block then
self:createMoveFX('right')
end
self.curX=self.curX+1
self:freshBlock('move')
end
if self.curX~=x0 then
self.spinLast=false
end
if self.gameEnv.shakeFX then
self.fieldOff.vx=self.gameEnv.shakeFX*.5
end
if auto then
if self.ctrlCount==0 then self.ctrlCount=1 end
else
self.ctrlCount=self.ctrlCount+1
end
end
function Player:act_insDown()
if self.cur and self.curY>self.ghoY then
local ENV=self.gameEnv
local CB=self.cur.bk
if ENV.dropFX and ENV.block and self.curY-self.ghoY-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-self.ghoY-#CB+1)
end
if ENV.shakeFX then
self.fieldOff.vy=ENV.shakeFX*.5
end
self.curY=self.ghoY
self.lockDelay=ENV.lock
self.spinLast=false
self:freshBlock('fresh')
end
end
function Player:act_down1()
if self.cur and self.curY>self.ghoY then
if self.gameEnv.moveFX and self.gameEnv.block then
self:createMoveFX('down')
end
self.curY=self.curY-1
self:freshBlock('fresh')
self.spinLast=false
end
end
function Player:act_down4()
if self.cur and self.curY>self.ghoY then
local y=max(self.curY-4,self.ghoY)
local CB=self.cur.bk
if self.gameEnv.dropFX and self.gameEnv.block and self.curY-y-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-y-#CB+1)
end
self.curY=y
self:freshBlock('fresh')
self.spinLast=false
end
end
function Player:act_down10()
if self.cur and self.curY>self.ghoY then
local y=max(self.curY-10,self.ghoY)
local CB=self.cur.bk
if self.gameEnv.dropFX and self.gameEnv.block and self.curY-y-#CB>-1 then
self:createDropFX(self.curX,self.curY-1,#CB[1],self.curY-y-#CB+1)
end
self.curY=y
self:freshBlock('fresh')
self.spinLast=false
end
end
function Player:act_dropLeft()
if not self.cur then return end
self:act_insLeft()
self:act_hardDrop()
end
function Player:act_dropRight()
if not self.cur then return end
self:act_insRight()
self:act_hardDrop()
end
function Player:act_zangiLeft()
if not self.cur then return end
self:act_insLeft()
self:act_insDown()
self:act_insRight()
self:act_hardDrop()
end
function Player:act_zangiRight()
if not self.cur then return end
self:act_insRight()
self:act_insDown()
self:act_insLeft()
self:act_hardDrop()
end
Player.actList={
Player.act_moveLeft, --1
Player.act_moveRight, --2
Player.act_rotRight, --3
Player.act_rotLeft, --4
Player.act_rot180, --5
Player.act_hardDrop, --6
Player.act_softDrop, --7
Player.act_hold, --8
Player.act_func1, --9
Player.act_func2, --10
Player.act_insLeft, --11
Player.act_insRight, --12
Player.act_insDown, --13
Player.act_down1, --14
Player.act_down4, --15
Player.act_down10, --16
Player.act_dropLeft, --17
Player.act_dropRight, --18
Player.act_zangiLeft, --19
Player.act_zangiRight, --20
}
----------------------------------------------------
return Player