2074 lines
48 KiB
Lua
2074 lines
48 KiB
Lua
-------------------------------------------------
|
||
--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,SKIN=SFX,BGM,VOC,VIB,SYSFX,SKIN
|
||
local FREEROW,TABLE,TEXT,NET,TASK=FREEROW,TABLE,TEXT,NET,TASK
|
||
local PLAYERS,PLY_ALIVE,GAME=PLAYERS,PLY_ALIVE,GAME
|
||
|
||
local kickList=require"parts.kickList"
|
||
local ply_draw=require"parts.player.draw"
|
||
local ply_update=require"parts.player.update"
|
||
|
||
--------------------------<FX>--------------------------
|
||
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: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,
|
||
SKIN.curText[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
|
||
--------------------------</FX>--------------------------
|
||
|
||
--------------------------<Method>--------------------------
|
||
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(7,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.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=kickList[RSname]
|
||
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: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.lockDelay<l0 and self.freshTime>0 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]<self.minY then
|
||
self.minY=self.curY+self.cur.sc[1]
|
||
self.dropDelay=d0
|
||
self.lockDelay=l0
|
||
end
|
||
else
|
||
if self.curY+self.cur.sc[1]<self.minY then
|
||
self.minY=self.curY+self.cur.sc[1]
|
||
if self.lockDelay<l0 and self.freshTime>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
|
||
|
||
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 _=self.keyPressing
|
||
--IMS
|
||
if self.gameEnv.ims and(_[1]and self.movDir==-1 or _[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 _[5]then
|
||
self:spin(2,true)
|
||
else
|
||
if _[3]then
|
||
if _[4]then
|
||
self:spin(2,true)
|
||
else
|
||
self:spin(1,true)
|
||
end
|
||
elseif _[4]then
|
||
self:spin(3,true)
|
||
end
|
||
end
|
||
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 kickData=self.RS[self.cur.id]
|
||
if type(kickData)=='table'then
|
||
local idir=(self.cur.dir+d)%4
|
||
kickData=kickData[self.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[self.cur.id][idir]
|
||
local isc=SCS[self.cur.id][idir]
|
||
local ix,iy=self.curX+self.cur.sc[2]-isc[2],self.curY+self.cur.sc[1]-isc[1]
|
||
for test=1,#kickData do
|
||
local x,y=ix+kickData[test][1],iy+kickData[test][2]
|
||
if not self:ifoverlap(icb,x,y)and(self.freshTime>0 or kickData[test][2]<=0)then
|
||
ix,iy=x,y
|
||
if self.gameEnv.moveFX and self.gameEnv.block then
|
||
self:createMoveFX()
|
||
end
|
||
self.curX,self.curY,self.cur.dir=ix,iy,idir
|
||
self.cur.sc,self.cur.bk=isc,icb
|
||
self.spinLast=test==2 and 0 or 1
|
||
|
||
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
|
||
|
||
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<ENV.holdCount and self.nextQueue[1]then--Skip
|
||
local C=self.cur
|
||
ins(self.holdQueue,self:getBlock(C.id,C.name,C.color))
|
||
|
||
local t=self.holdTime
|
||
self:popNext(true)
|
||
self.holdTime=t
|
||
else--Hold
|
||
local C,H=self.cur,self.holdQueue[1]
|
||
|
||
--Finesse check
|
||
if H and C and H.id==C.id and H.name==C.name then
|
||
self.ctrlCount=self.ctrlCount+1
|
||
elseif self.ctrlCount<=1 then
|
||
self.ctrlCount=0
|
||
end
|
||
|
||
if ENV.phyHold and C and not ifpre then--Physical hold
|
||
local x,y=self.curX,self.curY
|
||
x=x+(#C.bk[1]-#H.bk[1])*.5
|
||
y=y+(#C.bk-#H.bk)*.5
|
||
|
||
local iki=phyHoldKickX[x==int(x)]
|
||
for Y=int(y),ceil(y+.5)do
|
||
for i=1,#iki do
|
||
local X=x+iki[i]
|
||
if not self:ifoverlap(H.bk,X,Y)then
|
||
x,y=X,Y
|
||
goto BREAK_success
|
||
end
|
||
end
|
||
end
|
||
--<for-else> All test failed, interrupt with sound
|
||
SFX.play('finesseError')
|
||
do return end
|
||
--<for-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=SCS[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=SCS[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(self)
|
||
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 N<A.amount then
|
||
A.amount=A.amount-O
|
||
else
|
||
A.sent,A.time=true,0
|
||
end
|
||
off=off+O
|
||
self.atkBufferSum=self.atkBufferSum-O
|
||
N=N-O
|
||
end
|
||
end
|
||
return off
|
||
end
|
||
do--Player.drop(self)--Place piece
|
||
local clearSCR={80,200,400}--Techrash:1K; B2Bmul:1.3/1.8
|
||
local spinSCR={
|
||
{200,750,1300,2000},--Z
|
||
{200,750,1300,2000},--S
|
||
{220,700,1300,2000},--L
|
||
{220,700,1300,2000},--J
|
||
{250,800,1400,2000},--T
|
||
{260,900,1600,4500,7000},--O
|
||
{300,1200,1700,4000,6000},--I
|
||
{220,800,2000,3000,8000,26000},--Else
|
||
}--B2B*=1.2; B3B*=2.0; Mini*=.6
|
||
local b2bPoint={50,100,180,800,1000,9999}
|
||
|
||
local b2bATK={3,5,8,12,18,26}
|
||
local reAtk={0,0,1,1,1,2,2,3,3}
|
||
local reDef={0,1,1,2,3,3,4,4,5}
|
||
|
||
local spinVoice={'zspin','sspin','jspin','lspin','tspin','ospin','ispin','zspin','sspin','pspin','qspin','fspin','espin','tspin','uspin','vspin','wspin','xspin','jspin','lspin','rspin','yspin','hspin','nspin','ispin'}
|
||
local clearVoice={'single','double','triple','techrash','pentcrash','hexcrash'}
|
||
local spinSFX={[0]='spin_0','spin_1','spin_2'}
|
||
local clearSFX={'clear_1','clear_2','clear_3'}
|
||
local renSFX={}for i=1,11 do renSFX[i]='ren_'..i end
|
||
local finesseList={
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,2,2,1,1,2,3,2,2},
|
||
1,2
|
||
},--Z
|
||
1,--S
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,2,3,2,1,2,3,3,2},
|
||
{3,4,3,2,3,4,4,3},
|
||
{2,3,2,1,2,3,3,2,2},
|
||
},--J
|
||
3,--L
|
||
3,--T
|
||
{
|
||
{1,2,2,1,0,1,2,2,1},
|
||
1,1,1
|
||
},--O
|
||
{
|
||
{1,2,1,0,1,2,1},
|
||
{2,2,2,2,1,1,2,2,2,2},
|
||
1,2
|
||
},--I
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,3,2,1,2,3,3,2},
|
||
1,2
|
||
},--Z5
|
||
8,--S5
|
||
3,--self
|
||
3,--Q
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,3,2,1,2,3,3,2},
|
||
{3,4,3,2,3,4,4,3},
|
||
2
|
||
},--F
|
||
12,--E
|
||
12,--T5
|
||
3,--U
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,3,3,2,1,2,3,2},
|
||
{3,4,4,3,2,3,4,3},
|
||
{2,3,2,1,2,3,3,2},
|
||
},--V
|
||
12,--W
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
1,1,1
|
||
},--X
|
||
{
|
||
{1,2,1,0,1,2,1},
|
||
{2,2,3,2,1,2,3,2,2},
|
||
{3,4,3,2,3,4,3},
|
||
2,
|
||
},--J5
|
||
19,--L5
|
||
19,--R
|
||
19,--Y
|
||
19,--N
|
||
19,--H
|
||
{
|
||
{1,1,0,1,2,1},
|
||
{2,3,2,2,1,2,3,2,3,2},
|
||
1,2
|
||
},--I5
|
||
{
|
||
{1,2,1,0,1,2,2,1},
|
||
{2,2,3,2,1,2,3,3,2,2},
|
||
1,2
|
||
},--I3
|
||
{
|
||
{1,2,2,1,0,1,2,2,1},
|
||
{2,3,3,2,1,2,3,3,2},
|
||
{3,4,4,3,2,3,4,4,3},
|
||
2
|
||
},--C
|
||
{
|
||
{1,2,2,1,0,1,2,2,1},
|
||
{2,2,3,2,1,1,2,3,2,2},
|
||
1,2
|
||
},--I2
|
||
{
|
||
{1,2,2,1,0,1,2,3,2,1},
|
||
1,1,1
|
||
},--O1
|
||
}
|
||
for k,v in next,finesseList do
|
||
if type(v)=='table'then
|
||
for d,l in next,v do
|
||
if type(l)=='number'then
|
||
v[d]=v[l]
|
||
end
|
||
end
|
||
else
|
||
finesseList[k]=finesseList[v]
|
||
end
|
||
end
|
||
|
||
function Player:drop()
|
||
local _
|
||
local CHN=VOC.getFreeChannel()
|
||
self.dropTime[11]=ins(self.dropTime,1,self.frameRun)--Update speed dial
|
||
local ENV=self.gameEnv
|
||
local Stat=self.stat
|
||
local piece=self.lastPiece
|
||
|
||
local finish
|
||
local cmb=self.combo
|
||
local C,CB,CX,CY=self.cur,self.cur.bk,self.curX,self.curY
|
||
local clear--If clear with no line fall
|
||
local cc,gbcc=0,0--Row/garbage-row cleared,full-part
|
||
local atk,exblock=0,0--Attack & extra defense
|
||
local send,off=0,0--Sending lines remain & offset
|
||
local cscore,sendTime=10,0--Score & send Time
|
||
local dospin,mini=0
|
||
|
||
piece.id,piece.name=C.id,C.name
|
||
self.waiting=ENV.wait
|
||
|
||
--Tri-corner spin check
|
||
if self.spinLast then
|
||
if C.id<6 then
|
||
local x,y=CX+C.sc[2],CY+C.sc[1]
|
||
local c=0
|
||
if self:solid(x-1,y+1)then c=c+1 end
|
||
if self:solid(x+1,y+1)then c=c+1 end
|
||
if c~=0 then
|
||
if self:solid(x-1,y-1)then c=c+1 end
|
||
if self:solid(x+1,y-1)then c=c+1 end
|
||
if c>2 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 self.clearedRow={}end
|
||
|
||
--Check line clear
|
||
for i=1,#CB do
|
||
local h=CY+i-2
|
||
|
||
--Bomb trigger
|
||
if h>0 and self.field[h]and self.clearedRow[cc]~=h then
|
||
for x=1,#CB[1]do
|
||
if CB[i][x]and self.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 self.field[h][x]<=0 then
|
||
goto CONTINUE_notFull
|
||
end
|
||
end
|
||
cc=cc+1
|
||
self.clearingRow[cc]=h-cc+1
|
||
self.clearedRow[cc]=h
|
||
::CONTINUE_notFull::
|
||
end
|
||
|
||
--Create clearing FX
|
||
if cc>0 and ENV.clearFX then
|
||
local t=7-ENV.clearFX*1
|
||
for i=1,cc do
|
||
local y=self.clearedRow[i]
|
||
self:createClearingFX(y,t)
|
||
if ENV.splashFX then
|
||
self:createSplashFX(y)
|
||
end
|
||
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
|
||
if CY>ENV.fieldH-2 then
|
||
finesse=true
|
||
else
|
||
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=CX+x-1--Optimize
|
||
|
||
--Test the whole column of field to find roof
|
||
for testY=CY+y,#self.field do
|
||
if self:solid(testX,testY)then
|
||
finesse=true
|
||
goto BERAK_roofFound
|
||
end
|
||
end
|
||
end
|
||
::BERAK_roofFound::
|
||
end
|
||
|
||
--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
|
||
for i=#self.clearingRow,1,-1 do
|
||
if self.clearingRow[i]>#self.field then
|
||
rem(self.clearingRow)
|
||
else
|
||
break
|
||
end
|
||
end
|
||
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)
|
||
if self.b2b<50 and ENV.b2bKill then
|
||
finish=true
|
||
end
|
||
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 then
|
||
if #self.field==0 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>=#C.bk and(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 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:showText(
|
||
cscore,
|
||
(self.curX+C.sc[2]-5.5)*30,
|
||
(10-self.curY-C.sc[1])*30+self.fieldBeneath+self.fieldUp,
|
||
40-600/(cscore+20),
|
||
'score',
|
||
2
|
||
)
|
||
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
|
||
local t=ENV.mission[self.curMission]
|
||
local success
|
||
if t<5 then
|
||
if piece.row==t and not piece.spin then
|
||
success=true
|
||
end
|
||
elseif t<9 then
|
||
if piece.row==t-4 and piece.spin then
|
||
success=true
|
||
end
|
||
elseif t==9 then
|
||
if piece.pc then
|
||
success=true
|
||
end
|
||
elseif t<90 then
|
||
if piece.row==t%10 and piece.name==int(t/10)and piece.spin then
|
||
success=true
|
||
end
|
||
end
|
||
if success 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.score=Stat.score+cscore
|
||
Stat.piece=Stat.piece+1
|
||
Stat.row=Stat.row+cc
|
||
Stat.maxFinesseCombo=max(Stat.maxFinesseCombo,self.finesseCombo)
|
||
Stat.maxCombo=max(Stat.maxCombo,self.combo)
|
||
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()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,self.AI_keys)
|
||
end
|
||
--------------------------</Methods>--------------------------
|
||
|
||
--------------------------<Ticks>--------------------------
|
||
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
|
||
--------------------------</Ticks>--------------------------
|
||
|
||
--------------------------<Events>--------------------------
|
||
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.id=='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 self.type=='remote'and not force then self.waiting=1e99 return end
|
||
if self.life>0 and not force then self:revive()return 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>--------------------------
|
||
|
||
--------------------------<Actions>--------------------------
|
||
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()
|
||
if self.keyPressing[9]then
|
||
if self.gameEnv.swap then
|
||
self:changeAtkMode(3)
|
||
end
|
||
self.keyPressing[6]=false
|
||
elseif self.control and self.waiting==-1 and self.cur then
|
||
if self.curY>self.ghoY then
|
||
local CB=self.cur.bk
|
||
if self.gameEnv.dropFX and self.gameEnv.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 self.gameEnv.shakeFX then
|
||
self.fieldOff.vy=self.gameEnv.shakeFX*.6
|
||
self.fieldOff.va=self.fieldOff.va+self:getCenterX()*self.gameEnv.shakeFX*6e-4
|
||
end
|
||
self.lockDelay=-1
|
||
self:drop()
|
||
self.keyPressing[6]=false
|
||
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()
|
||
end
|
||
elseif self.gameEnv.initSkip then
|
||
rem(self.nextQueue,1)
|
||
self.newNext()
|
||
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
|
||
}
|
||
--------------------------</Actions>--------------------------
|
||
return Player |