local gc=love.graphics local mt=love.math local Timer=love.timer.getTime local int,ceil,abs,rnd,max,min=math.floor,math.ceil,math.abs,math.random,math.max,math.min local ins,rem=table.insert,table.remove local format=string.format local scr=scr--Screen camera local setFont=setFont ---------------------------------------------------- local gameEnv0={ noFly=false, das=10,arr=2, sddas=2,sdarr=2, ihs=true,irs=true,ims=true, swap=true, ghost=true,center=true, smooth=false,grid=false, bagLine=false, text=true, lockFX=2,dropFX=3, clearFX=2,shakeFX=3, drop=60,lock=60, wait=0,fall=0, _20G=false,bone=false, next=6, hold=true,oncehold=true, ospin=true, sequence="bag",bag={1,2,3,4,5,6,7}, freshMethod=NULL, face=NULL,skin=NULL, pushSpeed=3, block=true, visible="show", Fkey=NULL,puzzle=false, freshLimit=1e99,easyFresh=true, fine=false,fineKill=false, target=1e99,dropPiece=NULL, mindas=0,minarr=0,minsdarr=0, bg="none",bgm="race" } local b2bPoint={50,100,180,300} local b2bATK={3,5,8,10} local clearSCR={80,200,400} local spinSCR={--[blockName][row] {200,750,1300},--Z {200,750,1300},--S {220,700,1300},--L {220,700,1300},--J {250,800,1400},--T {260,900,1700},--O {300,1200,1700},--I {220,800,2000,3000},--Else } --B2BMUL:1.2/2.0 --Techrash:1K;MUL:1.3/1.8 --Mini*=.6 local reAtk={0,0,1,1,1,2,2,3,3} local reDef={0,1,1,2,3,3,4,4,5} local scs=require("parts/spinCenters") local kickList=require("parts/kickList") local finesseList={ [1]={ {1,2,1,0,1,2,2,1}, {2,2,2,1,1,2,3,2,2}, },--Z [3]={ {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}, },--L [6]={ {1,2,2,1,0,1,2,2,1}, },--O [7]={ {1,2,1,0,1,2,1}, {2,2,2,2,1,1,2,2,2,2}, },--I } finesseList[1][3],finesseList[1][4],finesseList[7][3],finesseList[7][4]=finesseList[1][1],finesseList[1][2],finesseList[7][1],finesseList[7][2]--"2-phase" SZI finesseList[2]=finesseList[1]--S=Z finesseList[4],finesseList[5]=finesseList[3],finesseList[3]--J=L=T local CCblockID={4,3,6,5,1,2,0} local freshPrepare={ none=NULL, bag=function(P) local bag=P.gameEnv.bag local L repeat L={}for i=1,#bag do L[i]=i end repeat P:getNext(bag[rem(L,P:RND(#L))])until not L[1] until #P.next>5 end, his4=function(P) local bag=P.gameEnv.bag local L=#bag P.his={bag[P:RND(L)],bag[P:RND(L)],bag[P:RND(L)],bag[P:RND(L)]} for _=1,6 do local i local j=0 repeat i=bag[P:RND(L)] j=j+1 until i~=P.his[1]and i~=P.his[2]and i~=P.his[3]and i~=P.his[4]or j==6 P:getNext(i) rem(P.his,1)P.his[4]=i end end, rnd=function(P) local bag=P.gameEnv.bag local L=#bag P:getNext(bag[P:RND(L)]) for i=1,5 do local count=0 local i repeat i=bag[P:RND(L)] count=count+1 until i~=P.next[#P.next].id or count>=L P:getNext(i) end end, loop=function(P) local bag=P.gameEnv.bag repeat for i=1,#bag do P:getNext(bag[i]) end until #P.next>5 end, fixed=function(P) local bag=P.gameEnv.bag for i=1,#bag do P:getNext(bag[i]) end end, } local freshMethod={ none=NULL, bag=function(P) if #P.next<6 then local bag0,bag=P.gameEnv.bag,{} for i=1,#bag0 do bag[i]=bag0[i]end repeat P:getNext(rem(bag,P:RND(#bag)))until not bag[1] end end, his4=function(P) if #P.next<6 then local bag=P.gameEnv.bag local L=#bag for n=1,4 do local j,i=0 repeat i=bag[P:RND(L)] j=j+1 until i~=P.his[1]and i~=P.his[2]and i~=P.his[3]and i~=P.his[4]or j==4 P:getNext(i) P.his[n]=i end end end, rnd=function(P) if #P.next<6 then local bag=P.gameEnv.bag local L=#bag for i=1,4 do local count=0 local i repeat i=bag[P:RND(L)] count=count+1 until i~=P.next[#P.next].id or count>=L P:getNext(i) end end end, loop=function(P) local bag=P.gameEnv.bag for i=1,#bag do P:getNext(bag[i]) end end, fixed=function(P) if P.cur or P.hd then return end P:lose() end, } local spinName={"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 clearName={"single","double","triple","techrash","pentcrash"} local spin_n={[0]="spin_0","spin_1","spin_2","spin_3","spin_3"} local clear_n={"clear_1","clear_2","clear_3","clear_4","clear_4"} local ren_n={}for i=1,11 do ren_n[i]="ren_"..i end ---------------------------------------------------- ---------------------------------------------------- local player={}--Player object local PLY={}--Lib ---------------------------------------------------- ---------------------------------------------------- local function updateLine(P,dt) local bf=P.atkBuffer for i=#bf,1,-1 do local A=bf[i] A.time=A.time+1 if not A.sent then if A.countdown>0 then A.countdown=max(A.countdown-game.garbageSpeed,0) end else if A.time>20 then rem(bf,i) end end end bf=P.fieldBeneath if bf>0 then P.fieldBeneath=max(bf-P.gameEnv.pushSpeed,0) end end local function updateFXs(P,dt) if P.stat.score>P.score1 then if P.stat.score-P.score1<10 then P.score1=P.score1+1 else P.score1=int(min(P.score1*.9+P.stat.score*.1+1)) end end --LockFX for i=#P.lockFX,1,-1 do local S=P.lockFX[i] S[3]=S[3]+S[4]*dt if S[3]>1 then rem(P.lockFX,i) end end --DropFX for i=#P.dropFX,1,-1 do local S=P.dropFX[i] S[6]=S[6]+S[7]*dt if S[6]>1 then rem(P.dropFX,i) end end --ClearFX for i=#P.clearFX,1,-1 do local S=P.clearFX[i] S[2]=S[2]+S[3]*dt if S[2]>1 then rem(P.clearFX,i) end end --Field shaking if P.gameEnv.shakeFX then local O=P.fieldOff O.vx,O.vy=O.vx*.8-abs(O.x)^1.2*(O.x>0 and .1 or -.1),O.vy*.8-abs(O.y)^1.2*(O.y>0 and .1 or -.1) O.x,O.y=O.x+O.vx,O.y+O.vy if abs(O.x)<.3 then O.x=0 end if abs(O.y)<.3 then O.y=0 end end if P.bonus then TEXT.update(P.bonus) end end local function updateTasks(P) local L=P.tasks for i=#L,1,-1 do if L[i].code(P,L[i].data)then rem(L,i)end end end local function Pupdate_alive(P,dt) if P.timing then P.stat.time=P.stat.time+dt end if P.keyRec then local _=game.frame local v=0 for i=2,10 do v=v+i*(i-1)*7.2/(_-P.keyTime[i])end P.keySpeed=P.keySpeed*.99+v*.1 v=0 for i=2,10 do v=v+i*(i-1)*7.2/(_-P.dropTime[i])end P.dropSpeed=P.dropSpeed*.99+v*.1 --Update speeds if modeEnv.royaleMode then if P.keyPressing[9]then P.swappingAtkMode=min(P.swappingAtkMode+2,30) else P.swappingAtkMode=P.swappingAtkMode+((#P.field>15 and P.swappingAtkMode>4 or P.swappingAtkMode>8)and -1 or 1) end end end if not P.human and P.control and P.waiting==-1 then local C=P.AI_keys P.AI_delay=P.AI_delay-1 if not C[1]then P.AI_stage=AIfunc[P.AI_mode][P.AI_stage](P,C) elseif P.AI_delay<=0 then P:pressKey(C[1])P:releaseKey(C[1]) rem(C,1) P.AI_delay=P.AI_delay0*2 end end --Fresh visible time if not P.keepVisible then for j=1,#P.field do for i=1,10 do if P.visTime[j][i]>0 then P.visTime[j][i]=P.visTime[j][i]-1 end end end end --Moving pressed if P.movDir~=0 then local das,arr=P.gameEnv.das,P.gameEnv.arr local mov=P.moving if P.waiting==-1 then if P.movDir==1 then if P.keyPressing[2]then if arr>0 then if mov==das+arr or mov==das then if not P.cur or P:ifoverlap(P.cur.bk,P.curX+1,P.curY)then mov=das+arr-1 else P.act.moveRight(P,true) mov=das end end mov=mov+1 else if mov==das then P.act.insRight(P,true) else mov=mov+1 end end if mov>=das and P.gameEnv.shakeFX and P.cur and P:ifoverlap(P.cur.bk,P.curX+1,P.curY)then P.fieldOff.vx=P.gameEnv.shakeFX*.5 end else P.movDir=0 end else if P.keyPressing[1]then if arr>0 then if mov==das+arr or mov==das then if not P.cur or P:ifoverlap(P.cur.bk,P.curX-1,P.curY)then mov=das+arr-1 else P.act.moveLeft(P,true) mov=das end end mov=mov+1 else if mov==das then P.act.insLeft(P,true) else mov=mov+1 end end if mov>=das and P.gameEnv.shakeFX and P.cur and P:ifoverlap(P.cur.bk,P.curX-1,P.curY)then P.fieldOff.vx=-P.gameEnv.shakeFX*.5 end else P.movDir=0 end end elseif mov1 then if P.gameEnv.sdarr>0 then if d%P.gameEnv.sdarr==0 then P.act.down1(P) end else P.act.insDown(P) end if P.gameEnv.shakeFX then P.fieldOff.vy=P.gameEnv.shakeFX*.3 end end else P.downing=0 end --Falling animation if P.falling>=0 then P.falling=P.falling-1 if P.falling>=0 then goto stop else local L=#P.clearingRow if P.human and P.gameEnv.fall>0 and #P.field+L>P.clearingRow[L]then SFX.play("fall")end P.clearingRow={} end end --Try spawn new block if not P.control then goto stop end if P.waiting==0 then P:popNext()end if P.waiting>=0 then P.waiting=P.waiting-1 goto stop end --Natural block falling if P.cur then if P.curY~=P.imgY then local D=P.dropDelay if D>1 then P.dropDelay=D-1 goto stop end if D==1 then P.curY=P.curY-1 else local _=P.curY-P.imgY--Max fall dist D=1/D--Fall dist if D<_ then P.curY=P.curY-D -- assert(P.curY==int(P.curY),"y:"..P.curY.." fall:"..D.." D_env:"..P.gameEnv.drop) else P.curY=P.imgY end end P:freshBlock(true,true) P.spinLast=false if P.imgY~=P.curY then P.dropDelay=P.gameEnv.drop elseif P.AI_mode=="CC"then CC_updateField(P) if not P.AIdata._20G and P.gameEnv.drop=0 then goto stop end P:drop() if P.AI_mode=="CC"then CC_updateField(P) end end end ::stop:: if P.b2b1==P.b2b then elseif P.b2b1=0 then P.falling=P.falling-1 if P.falling>=0 then goto stop else local L=#P.clearingRow if P.human and P.gameEnv.fall>0 and #P.field+L>P.clearingRow[L]then SFX.play("fall")end P.clearingRow={} end end ::stop:: if P.b2b1>0 then P.b2b1=max(0,P.b2b1*.92-1)end updateLine(P,dt) updateFXs(P,dt) updateTasks(P) end ---------------------------------------------------- ---------------------------------------------------- local frameColor={ [0]=color.white, color.lGreen, color.lBlue, color.lPurple, color.lOrange, } local attackColor={ {color.dGrey,color.white}, {color.grey,color.white}, {color.lPurple,color.white}, {color.lRed,color.white}, {color.dGreen,color.cyan}, } local RCPB={10,33,200,33,105,5,105,60} local function drawPixel(y,x,id) gc.draw(blockSkin[id],30*x-30,600-30*y) end local function drawDial(x,y,speed) gc.setColor(1,1,1) mStr(int(speed),x,y-18) gc.setLineWidth(4) gc.setColor(1,1,1,.4) gc.circle("line",x,y,30,10) gc.setLineWidth(2) gc.setColor(1,1,1,.6) gc.circle("line",x,y,30,10) gc.setColor(1,1,1,.6) gc.draw(IMG.dialNeedle,x,y,2.094+(speed<=175 and .02094*speed or 4.712-52.36/(speed-125)),nil,nil,5,4) end local function drawFXs(P) --LockFX for i=1,#P.lockFX do _=P.lockFX[i] if _[3]<.5 then gc.setColor(1,1,1,2*_[3]) gc.rectangle("fill",_[1],_[2],60*_[3],30) else gc.setColor(1,1,1,2-2*_[3]) gc.rectangle("fill",_[1]+30,_[2],60*_[3]-60,30) end end --DropFX for i=1,#P.dropFX do _=P.dropFX[i] gc.setColor(1,1,1,.6-_[6]*.6) for x=_[2],_[4]do for y=_[5],_[3]do drawPixel(y,x,_[1]) end end end --ClearFX for i=1,#P.clearFX do local S=P.clearFX[i] local t=S[2] local x=t<.3 and 1-(3.3333*t-1)^2 or 1 local y=t<.2 and 5*t or 1-1.25*(t-.2) gc.setColor(1,1,1,y) gc.rectangle("fill",150-x*150,615-S[1]*30-y*15,300*x,y*30) end end local function Pdraw_norm(P) local _ gc.push("transform") --Camera gc.translate(P.x,P.y)gc.scale(P.size) --Fill field gc.translate(150+P.fieldOff.x,70+P.fieldOff.y) gc.setColor(0,0,0,.6)gc.rectangle("fill",0,-10,300,610) --Grid if P.gameEnv.grid then gc.setLineWidth(1) gc.setColor(1,1,1,.2) for x=1,9 do gc.line(30*x,-10,30*x,600)end for y=0,19 do y=30*(y-int(P.fieldBeneath/30))+P.fieldBeneath gc.line(0,y,300,y) end end --In-field things gc.setLineWidth(2) gc.translate(0,P.fieldBeneath) gc.setScissor(scr.x+(P.absFieldX+P.fieldOff.x)*scr.k,scr.y+(P.absFieldY+P.fieldOff.y)*scr.k,300*P.size*scr.k,610*P.size*scr.k) if P.falling==-1 then--Field block only for j=int(P.fieldBeneath/30+1),#P.field do for i=1,10 do if P.field[j][i]>0 then gc.setColor(1,1,1,min(P.visTime[j][i]*.05,1)) drawPixel(j,i,P.field[j][i]) end end end else--Field with falling animation local dy,stepY=0,P.gameEnv.smooth and(P.falling/(P.gameEnv.fall+1))^2.5*30 or 30 local A=P.falling/P.gameEnv.fall local h,H=1,#P.field for j=int(P.fieldBeneath/30+1),H do while j==P.clearingRow[h]do h=h+1 dy=dy+stepY gc.translate(0,-stepY) gc.setColor(1,1,1,A) gc.rectangle("fill",0,630-30*j,300,stepY) end for i=1,10 do if P.field[j][i]>0 then gc.setColor(1,1,1,min(P.visTime[j][i]*.05,1)) drawPixel(j,i,P.field[j][i]) end end end gc.translate(0,dy) end drawFXs(P) if P.cur and P.waiting==-1 then local curColor=P.cur.color --Ghost if P.gameEnv.ghost then gc.setColor(1,1,1,.3) for i=1,P.r do for j=1,P.c do if P.cur.bk[i][j]then drawPixel(i+P.imgY-1,j+P.curX-1,curColor) end end end end local dy=P.gameEnv.smooth and P.imgY~=P.curY and (min(P.dropDelay,1e99)/P.gameEnv.drop-1)*30 or 0 gc.translate(0,-dy) local trans=P.lockDelay/P.gameEnv.lock if P.gameEnv.block then --White Boarder(indicate lockdelay) SHADER.alpha:send("a",trans) gc.setShader(SHADER.alpha) _=blockSkin[curColor] for i=1,P.r do for j=1,P.c do if P.cur.bk[i][j]then local x=30*(j+P.curX)-60-3 local y=630-30*(i+P.curY)-3 gc.draw(_,x,y)gc.draw(_,x+6,y+6) gc.draw(_,x+6,y)gc.draw(_,x,y+6) end end end gc.setShader() --Block gc.setColor(1,1,1) for i=1,P.r do for j=1,P.c do if P.cur.bk[i][j]then drawPixel(i+P.curY-1,j+P.curX-1,curColor) end end end end --Rotate center if P.gameEnv.center then gc.setColor(1,1,1,trans) local x=30*(P.curX+P.sc[2])-15 gc.draw(IMG.spinCenter,x,600-30*(P.curY+P.sc[1])+15,nil,nil,nil,4,4) if P.gameEnv.ghost then gc.translate(0,dy) gc.setColor(1,1,1,.5) gc.draw(IMG.spinCenter,x,600-30*(P.imgY+P.sc[1])+15,nil,nil,nil,4,4) goto E end end gc.translate(0,dy) end ::E:: gc.setScissor() gc.translate(0,-P.fieldBeneath) gc.setColor(P.frameColor) gc.rectangle("line",-1,-11,302,612)--Boarder gc.rectangle("line",301,0,15,601)--AtkBuffer boarder gc.rectangle("line",-16,-3,15,604)--B2b bar boarder --LockDelay indicator if P.gameEnv.easyFresh then gc.setColor(1,1,1) else gc.setColor(1,.26,.26) end if P.lockDelay>=0 then gc.rectangle("fill",0,602,300*P.lockDelay/P.gameEnv.lock,6)--Lock delay indicator end _=3 for i=1,min(P.gameEnv.freshLimit-P.freshTime,15)do gc.rectangle("fill",_,615,14,5) _=_+20 end --Buffer line local h=0 for i=1,#P.atkBuffer do local A=P.atkBuffer[i] local bar=A.amount*30 if h+bar>600 then bar=600-h end if not A.sent then --Appear if A.time<20 then bar=bar*(20*A.time)^.5*.05 end if A.countdown>0 then --Timing gc.setColor(attackColor[A.lv][1]) gc.rectangle("fill",303,599-h,11,-bar+3) gc.setColor(attackColor[A.lv][2]) gc.rectangle("fill",303,599-h+(-bar+3),11,-(-bar+3)*(1-A.countdown/A.cd0)) else --Warning local t=math.sin((Timer()-i)*30)*.5+.5 local c1,c2=attackColor[A.lv][1],attackColor[A.lv][2] gc.setColor(c1[1]*t+c2[1]*(1-t),c1[2]*t+c2[2]*(1-t),c1[3]*t+c2[3]*(1-t)) gc.rectangle("fill",303,599-h,11,-bar+3) end else gc.setColor(attackColor[A.lv][1]) bar=bar*(20-A.time)*.05 gc.rectangle("fill",303,599-h,11,-bar+2) --Disappear end h=h+bar end --B2B indictator local a,b=P.b2b,P.b2b1 if a>b then a,b=b,a end gc.setColor(.8,1,.2) gc.rectangle("fill",-14,599,11,-b*.5) gc.setColor(P.b2b<40 and color.white or P.b2b<=1e3 and color.lRed or color.lBlue) gc.rectangle("fill",-14,599,11,-a*.5) gc.setColor(1,1,1) if Timer()%.5<.3 then gc.rectangle("fill",-15,b<40 and 578.5 or 98.5,13,3) end gc.translate(-P.fieldOff.x,-P.fieldOff.y) --Draw Hold if P.gameEnv.hold then gc.setColor(0,0,0,.4)gc.rectangle("fill",-140,36,124,80) gc.setColor(1,1,1)gc.rectangle("line",-140,36,124,80) mText(drawableText.hold,-78,-15) if P.hd then if P.holded then gc.setColor(.6,.5,.5)end local B=P.hd.bk for i=1,#B do for j=1,#B[1]do if B[i][j]then drawPixel(i+17.5-#B*.5,j-2.6-#B[1]*.5,P.hd.color) end end end end end --Draw Next(s) local N=P.gameEnv.next*72 if P.gameEnv.next>0 then gc.setColor(0,0,0,.4)gc.rectangle("fill",316,36,124,N) gc.setColor(1,1,1)gc.rectangle("line",316,36,124,N) mText(drawableText.next,378,-15) N=1 while N<=P.gameEnv.next and P.next[N]do local b,c=P.next[N].bk,P.next[N].color for i=1,#b do for j=1,#b[1] do if b[i][j]then drawPixel(i+20-2.4*N-#b*.5,j+12.6-#b[1]*.5,c) end end end N=N+1 end end --Draw Bagline(s) if P.gameEnv.bagLine then local L=P.gameEnv.bagLen local C=-P.pieceCount%L--Phase gc.setColor(.8,.5,.5) for i=C,N-1,L do local y=72*i+36 gc.line(318+P.fieldOff.x,y,438,y) end end --Draw starting counter gc.setColor(1,1,1) if game.frame<180 then local count=179-game.frame gc.push("transform") gc.translate(155,220) setFont(95) if count%60>45 then gc.scale(1+(count%60-45)^2*.01,1)end mStr(int(count/60+1),0,0) gc.pop() end --Bonus texts TEXT.draw(P.bonus) --Speed dials setFont(25) drawDial(360,510,P.dropSpeed) drawDial(405,565,P.keySpeed) gc.setColor(1,1,1) gc.draw(drawableText.bpm,390,480) gc.draw(drawableText.kpm,344,573) mStr(format("%.2f",P.stat.time),-81,518)--Time mStr(P.score1,-81,560)--Score --Display Ys -- gc.setLineWidth(6) -- if P.curY then gc.setColor(1,.4,0,.626)gc.line(0,611-P.curY*30,300,611-P.curY*30)end -- if P.imgY then gc.setColor(0,1,.4,.626)gc.line(0,615-P.imgY*30,300,615-P.imgY*30)end -- if P.minY then gc.setColor(0,.4,1,.626)gc.line(0,619-P.minY*30,300,619-P.minY*30)end --Other messages gc.setColor(1,1,1) curMode.mesDisp(P) if modeEnv.royaleMode then if P.atkMode then gc.setColor(1,.8,0,P.swappingAtkMode*.02) gc.rectangle("fill",RCPB[2*P.atkMode-1],RCPB[2*P.atkMode],90,35,8,4) end gc.setColor(1,1,1,P.swappingAtkMode*.025) setFont(18) for i=1,4 do gc.rectangle("line",RCPB[2*i-1],RCPB[2*i],90,35,8,4) mStr(text.atkModeName[i],RCPB[2*i-1]+45,RCPB[2*i]+3) end end gc.pop() end local function Pdraw_small(P) --Draw content P.frameWait=P.frameWait-1 if P.frameWait==0 then P.frameWait=10 gc.setCanvas(P.canvas) gc.clear(0,0,0,.4) gc.push("transform") gc.origin() gc.setColor(1,1,1,P.result and max(20-P.endCounter,0)*.05 or 1) --Field local F=P.field for j=1,#F do for i=1,10 do if F[j][i]>0 then gc.draw(blockSkinMini[F[j][i]],6*i-6,120-6*j) end end end --Draw boarder if P.alive then gc.setLineWidth(2) gc.setColor(P.frameColor) gc.rectangle("line",1,1,58,118) end if modeEnv.royaleMode then gc.setColor(1,1,1) for i=1,P.strength do gc.draw(IMG.badgeIcon,12*i-7,4,nil,.5) end end if P.result then gc.setColor(1,1,1,min(P.endCounter,60)*.01) setFont(17)mStr(P.result,32,47) setFont(15)mStr(P.modeData.event,30,82) end gc.pop() gc.setCanvas() end --Draw Canvas gc.setColor(1,1,1) gc.draw(P.canvas,P.x,P.y,nil,P.size*10) if P.killMark then gc.setLineWidth(3) gc.setColor(1,0,0,min(P.endCounter,25)*.04) gc.circle("line",P.centerX,P.centerY,(840-20*min(P.endCounter,30))*P.size) end setFont(30) end local function Pdraw_demo(P) local _ local curColor=P.cur.color --Camera gc.push("transform") gc.translate(P.x,P.y)gc.scale(P.size)gc.translate(P.fieldOff.x,P.fieldOff.y) --Frame gc.setColor(.1,.1,.1,.8) gc.rectangle("fill",0,0,300,600) gc.setLineWidth(2) gc.setColor(1,1,1) gc.rectangle("line",-1,-1,302,602) if P.falling==-1 then --Field block only for j=int(P.fieldBeneath/30+1),#P.field do for i=1,10 do if P.field[j][i]>0 then gc.setColor(1,1,1,min(P.visTime[j][i]*.05,1)) drawPixel(j,i,P.field[j][i]) end end end else --Field with falling animation local dy,stepY=0,P.gameEnv.smooth and(P.falling/(P.gameEnv.fall+1))^2.5*30 or 30 local A=P.falling/P.gameEnv.fall local h,H=1,#P.field for j=int(P.fieldBeneath/30+1),H do while j==P.clearingRow[h]do h=h+1 dy=dy+stepY gc.translate(0,-stepY) gc.setColor(1,1,1,A) gc.rectangle("fill",0,630-30*j,300,stepY) end for i=1,10 do if P.field[j][i]>0 then gc.setColor(1,1,1,min(P.visTime[j][i]*.05,1)) drawPixel(j,i,P.field[j][i]) end end end gc.translate(0,dy) end drawFXs(P) if P.cur and P.waiting==-1 then --Draw ghost gc.setColor(1,1,1,.3) for i=1,P.r do for j=1,P.c do if P.cur.bk[i][j]then drawPixel(i+P.imgY-1,j+P.curX-1,curColor) end end end --Draw block gc.setColor(1,1,1) for i=1,P.r do for j=1,P.c do if P.cur.bk[i][j]then drawPixel(i+P.curY-1,j+P.curX-1,curColor) end end end end --Draw hold local blockImg=TEXTURE.miniBlock if P.hd then local id=P.hd.id _=P.color[id] gc.setColor(_[1],_[2],_[3],.3) _=blockImg[id] gc.draw(_,15,30,nil,16,nil,0,_:getHeight()*.5) end --Draw next local N=1 while N<=P.gameEnv.next and P.next[N]do local id=P.next[N].id _=P.color[id] gc.setColor(_[1],_[2],_[3],.3) _=blockImg[id] gc.draw(_,285,40*N-10,nil,16,nil,_:getWidth(),_:getHeight()*.5) N=N+1 end gc.setColor(1,1,1) gc.translate(-P.fieldOff.x,-P.fieldOff.y) TEXT.draw(P.bonus) gc.pop() end ---------------------------------------------------- ---------------------------------------------------- function player.showText(P,text,dx,dy,font,style,spd,stop) if P.gameEnv.text then P.bonus[#P.bonus+1]=TEXT.getText(text,150+dx,300+dy,font*P.size,style,spd,stop) end end function player.showTextF(P,text,dx,dy,font,style,spd,stop) P.bonus[#P.bonus+1]=TEXT.getText(text,150+dx,300+dy,font*P.size,style,spd,stop) end local function without(L,e) for i=1,#L do if L[i]==e then return end end return true end function player.createLockFX(P) local BK=P.cur.bk local t=12-P.gameEnv.lockFX*3 for i=1,P.r do local y=P.curY+i-1 if without(P.clearedRow,y)then y=600-30*y for j=1,P.c do if BK[i][j]then ins(P.lockFX,{30*(P.curX+j-2),y,0,t}) end end end end end function player.createDropFX(P,x1,y1,x2,y2)--x1y2! if P.gameEnv.block and y1>=y2 then P.dropFX[#P.dropFX+1]={P.cur.color,x1,y1,x2,y2,0,12-2*P.gameEnv.dropFX} end end function player.createBeam(P,R,send,time,target,color,clear,spin,combo) local x1,y1,x2,y2 if P.small then x1,y1=P.centerX,P.centerY else x1,y1=P.x+(30*(P.curX+P.sc[2])-30+15+150)*P.size,P.y+(600-30*(P.curY+P.sc[1])+15+70)*P.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 radius,corner local a,r,g,b=1,unpack(SKIN.libColor[color]) if clear>10 then radius=10+3*send+100/(target+4) local t=clear%10 if t==1 then corner=3 r=.3+r*.4 g=.3+g*.4 b=.3+b*.4 elseif t==2 then corner=5 r=.5+r*.5 g=.5+g*.5 b=.5+b*.5 elseif t<6 then corner=6 r=.6+r*.4 g=.6+g*.4 b=.6+b*.4 else corner=20 r=.8+r*.2 g=.8+g*.2 b=.8+b*.2 end else if combo>3 then radius=min(15+combo,30) corner=3 else radius=30 corner=4 end r=1-r*.3 g=1-g*.3 b=1-b*.3 end if modeEnv.royaleMode and not(P.human or R.human)then radius=radius*.4 a=.35 end FX_attack[#FX_attack+1]={ x=x1,y=y1,--Current pos x1=x1,y1=y1,--Start pos x2=x2,y2=y2,--End pos rad=radius*(setting.atkFX+3)*.12, corner=corner, type=type==1 and"fill"or"line", r=r,g=g,b=b,a=a*(setting.atkFX+5)*.1, t=0, drag={},--Afterimage coordinate list } end function player.newTask(P,code,data) local L=P.tasks L[#L+1]={ code=code, data=data, } end ---------------------------------------------------- ---------------------------------------------------- function player.RND(P,a,b) local R=P.randGen return R:random(a,b) end local function getNewStatTable() local T={ time=0,score=0, key=0,rotate=0,hold=0, extraPiece=0,extraRate=0, piece=0,row=0,dig=0, atk=0,digatk=0, send=0,recv=0,pend=0,off=0, clear={},clears={},spin={},spins={}, pc=0,hpc=0,b2b=0,b3b=0, } for i=1,25 do T.clear[i]={0,0,0,0,0} T.spin[i]={0,0,0,0,0} T.clears[i]=0 T.spins[i]=0 end return T end function player.solid(P,x,y) if x<1 or x>10 or y<1 then return true end if y>#P.field then return false end return P.field[y][x]>0 end function player.ifoverlap(P,bk,x,y) local C=#bk[1] if x<1 or x+C>11 or y<1 then return true end if y>#P.field then return end for i=1,#bk do if P.field[y+i-1]then for j=1,C do if bk[i][j]and P.field[y+i-1][x+j-1]>0 then return true end end end end end function player.ckfull(P,i) for j=1,10 do if P.field[i][j]<=0 then return end end return true end function player.finesseError(P,rate) P.stat.extraPiece=P.stat.extraPiece+1 P.stat.extraRate=P.stat.extraRate+rate if P.human then if P.gameEnv.fineKill then SFX.play("finesseError_long",.6) P:lose() elseif setting.fine then SFX.play("finesseError",.8) end elseif P.gameEnv.fineKill then P:lose() end end function player.attack(P,R,send,time,...) if setting.atkFX>0 then P:createBeam(R,send,time,...) end R.lastRecv=P if R.atkBuffer.sum<20 then local B=R.atkBuffer if send>20-B.sum then send=20-B.sum end--No more then 20 local m,k=#B,1 while k<=m and time>B[k].countdown do k=k+1 end for i=m,k,-1 do B[i+1]=B[i] end B[k]={ pos=P:RND(10), amount=send, countdown=time, cd0=time, time=0, sent=false, lv=min(int(send^.69),5), }--Sorted insert(by time) B.sum=B.sum+send R.stat.recv=R.stat.recv+send if R.human then SFX.play(send<4 and "blip_1"or"blip_2",min(send+1,5)*.1) end end end function player.garbageRelease(P) local n,flag=1 while true do local A=P.atkBuffer[n] if A and A.countdown<=0 and not A.sent then P:garbageRise(12+A.lv,A.amount,A.pos) P.atkBuffer.sum=P.atkBuffer.sum-A.amount A.sent,A.time=true,0 P.stat.pend=P.stat.pend+A.amount n=n+1 flag=true else break end end if flag and P.AI_mode=="CC"then CC_updateField(P)end end function player.garbageRise(P,color,amount,pos) local _ local t=P.showTime*2 for _=1,amount do ins(P.field,1,freeRow.get(color)) ins(P.visTime,1,freeRow.get(t)) P.field[1][pos]=0 end P.fieldBeneath=P.fieldBeneath+amount*30 if P.cur then P.curY=P.curY+amount P.imgY=P.imgY+amount end P.garbageBeneath=P.garbageBeneath+amount for i=1,#P.clearingRow do P.clearingRow[i]=P.clearingRow[i]+amount end for i=1,#P.lockFX do _=P.lockFX[i] _[2]=_[2]-30*amount--Shift 30px per line cleared end for i=1,#P.dropFX do _=P.dropFX[i] _[3],_[5]=_[3]+amount,_[5]+amount end if #P.field>40 then P:lose()end end local invList={2,1,4,3,5,6,7} function player.pushLine(P,L,mir) local S=P.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(P.field,1,r) ins(P.visTime,1,freeRow.get(20)) end P.fieldBeneath=P.fieldBeneath+120 P.curY=P.curY+#L P.imgY=P.imgY+#L P:freshBlock(true,false) end function player.pushNext(P,L,mir) for i=1,#L do P:getNext(mir and invList[L[i]]or L[i]) end end function player.freshTarget(P) if P.atkMode==1 then if not P.atking or not P.atking.alive or rnd()<.1 then P:changeAtk(randomTarget(P)) end elseif P.atkMode==2 then P:changeAtk(P~=game.mostBadge and game.mostBadge or game.secBadge or randomTarget(P)) elseif P.atkMode==3 then P:changeAtk(P~=game.mostDangerous and game.mostDangerous or game.secDangerous or randomTarget(P)) elseif P.atkMode==4 then for i=1,#P.atker do if not P.atker[i].alive then rem(P.atker,i) return end end end end function player.changeAtkMode(P,m) if P.atkMode==m then return end P.atkMode=m if m==1 then P:changeAtk(randomTarget(P)) elseif m==2 or m==3 then P:freshTarget() elseif m==4 then P:changeAtk() end end function player.changeAtk(P,R) -- if not P.human then R=players[1]end--1vALL mode? if P.atking then local K=P.atking.atker for i=1,#K do if K[i]==P then rem(K,i) break end end end if R then P.atking=R R.atker[#R.atker+1]=P else P.atking=nil end end function player.freshBlock(P,keepGhost,control,system) if not keepGhost and P.cur then P.imgY=min(#P.field+1,P.curY) if P.gameEnv._20G or P.keyPressing[7]and P.gameEnv.sdarr==0 then local _=P.imgY --Move ghost to bottom while not P:ifoverlap(P.cur.bk,P.curX,P.imgY-1)do P.imgY=P.imgY-1 end --Cancel spinLast if _~=P.imgY then P.spinLast=false end --Create FX if dropped if P.curY>P.imgY then if P.gameEnv.dropFX then P:createDropFX(P.curX,P.curY+1,P.curX+P.c-1,P.imgY+P.r-1) end if P.gameEnv.shakeFX then P.fieldOff.vy=P.gameEnv.shakeFX*.5 end P.curY=P.imgY end else while not P:ifoverlap(P.cur.bk,P.curX,P.imgY-1)do P.imgY=P.imgY-1 end end end if control then if P.gameEnv.easyFresh then local d,d0=P.lockDelay,P.gameEnv.lock if d0 and not P.gameEnv.easyFresh then P.freshTime=P.freshTime+1 end if P.human then SFX.fieldPlay(ifpre and"prerotate"or P:ifoverlap(P.cur.bk,P.curX,P.curY+1)and P:ifoverlap(P.cur.bk,P.curX-1,P.curY)and P:ifoverlap(P.cur.bk,P.curX+1,P.curY)and"rotatekick"or"rotate",nil,P) end P.stat.rotate=P.stat.rotate+1 return end end else iki(P,d) end end function player.resetBlock(P) local C=P.cur local id=C.id local face=P.gameEnv.face[id] local sc=scs[id][face] P.sc=sc --Spin center P.dir=face --Block direction P.r,P.c=#C.bk,#C.bk[1] --Row/column P.curX=int(6-P.c*.5) local y=21+ceil(P.fieldBeneath/30) P.curY=y P.minY=y+sc[2] local _=P.keyPressing --IMS if P.gameEnv.ims and(_[1]and P.movDir==-1 or _[2]and P.movDir==1)and P.moving>=P.gameEnv.das then local x=P.curX+P.movDir if not P:ifoverlap(C.bk,x,y)then P.curX=x end end --IRS if P.gameEnv.irs then if _[5]then P:spin(2,true) else if _[3]then if _[4]then P:spin(2,true) else P:spin(1,true) end elseif _[4]then P:spin(3,true) end end end --Spawn SFX if P.human and id<8 then SFX.fplay("spawn_"..id,setting.spawn) end end function player.hold(P,ifpre) if not P.holded and (ifpre or P.waiting==-1) and P.gameEnv.hold then local H,C=P.hd,P.cur if not(H or C)then return end --Finesse check if H and C and H.id==C.id and H.name==C.name or P.ctrlCount>1 then P:finesseError(1) end P.holded=P.gameEnv.oncehold P.spinLast=false P.ctrlCount=0 P.spinSeq=0 P.cur,P.hd=H,C--Swap hold H,C=P.hd,P.cur if H then local hid=P.hd.id P.hd.bk=blocks[hid][P.gameEnv.face[hid]] end if not C then C=rem(P.next,1) P:newNext() if C then P.cur=C P.pieceCount=P.pieceCount+1 if P.AI_mode=="CC"then local next=P.next[P.AIdata.next] if next then BOT.addNext(P.AI_bot,CCblockID[next.id]) end end else P.holded=false end end if C then P:resetBlock() P:freshBlock(false,true) P.dropDelay=P.gameEnv.drop P.lockDelay=P.gameEnv.lock P.freshTime=max(P.freshTime-5,0) if P:ifoverlap(P.cur.bk,P.curX,P.curY)then P:lock()P:lose()end end if P.human then SFX.play(ifpre and"prehold"or"hold") end P.stat.hold=P.stat.hold+1 end end function player.getNext(P,n) local E=P.gameEnv P.next[#P.next+1]={bk=blocks[n][E.face[n]],id=n,color=E.bone and 12 or E.skin[n],name=n} end function player.popNext(P)--Pop next queue to hand P.holded=false P.spinLast=false P.spinSeq=0 P.ctrlCount=0 P.cur=rem(P.next,1) P:newNext() if P.cur then P.pieceCount=P.pieceCount+1 if P.AI_mode=="CC"then local next=P.next[P.AIdata.next] if next then BOT.addNext(P.AI_bot,CCblockID[next.id]) end end local _=P.keyPressing --IHS if _[8]and P.gameEnv.hold and P.gameEnv.ihs then P:hold(true) _[8]=false else P:resetBlock() end P.dropDelay=P.gameEnv.drop P.lockDelay=P.gameEnv.lock P.freshTime=0 if P.cur then if P:ifoverlap(P.cur.bk,P.curX,P.curY)then P:lock() P:lose() end P:freshBlock(false,true,true) end --IHdS if _[6]then P.act.hardDrop(P) _[6]=false end end end function player.cancel(P,N)--Cancel Garbage local k=0 --Pointer, attack bar selected local off=0 --Lines offseted local bf=P.atkBuffer ::R:: if bf.sum>0 then local A repeat k=k+1 A=bf[k] if not A then return off end until not A.sent if N>=A.amount then local O=A.amount--Cur Offset N=N-O off=off+O bf.sum=bf.sum-O A.sent,A.time=true,0 if N>0 then goto R end else off=off+N A.amount=A.amount-N bf.sum=bf.sum-N end end return off end function player.drop(P)--Place piece local _ local CHN=VOC.getFreeChannel() P.dropTime[11]=ins(P.dropTime,1,game.frame)--Update speed dial local cmb=P.combo P.waiting=P.gameEnv.wait local STAT=P.stat local clear--If (perfect)clear 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=0,0--Score & send Time local dospin=0 local mini --Spin check if P.spinLast then --Tri-corner if P.cur.id<6 then local x,y=P.curX+P.sc[2],P.curY+P.sc[1] local c=0 if P:solid(x-1,y+1)then c=c+1 end if P:solid(x+1,y+1)then c=c+1 end if c==0 then goto NTC end if P:solid(x-1,y-1)then c=c+1 end if P:solid(x+1,y-1)then c=c+1 end if c>2 then dospin=dospin+1 end end ::NTC:: --Immovable if P:ifoverlap(P.cur.bk,P.curX-1,P.curY)and P:ifoverlap(P.cur.bk,P.curX+1,P.curY)and P:ifoverlap(P.cur.bk,P.curX,P.curY+1)then dospin=dospin+2 end end --Lock block to field P:lock() --Clear list of cleared-rows if P.clearedRow[1]then P.clearedRow={}end --Check rows to be cleared for i=0,P.r-1 do local h=P.curY+i if P:ckfull(h)then cc=cc+1 P.clearingRow[cc]=h-cc+1 P.clearedRow[cc]=h end end --Create clearing FX if cc>0 and P.gameEnv.clearFX then local l=P.clearedRow local t=6-P.gameEnv.clearFX*1.5 for i=1,cc do ins(P.clearFX,{l[i],0,t}) end end --Create locking FX if P.gameEnv.lockFX then if cc==0 then P:createLockFX() elseif P.lockFX[1]then P.lockFX={} end end --Final spin check if P.spinLast then if cc>0 then if dospin>0 then dospin=dospin+P.spinLast if dospin<2 then mini=P.cur.id<6 and cc<3 and cc7 then finesse=true elseif P.curY<=18 then local y0=P.curY local x,c=P.curX,P.c local B=P.cur.bk for x=1,c do local y for i=#B,1,-1 do if B[i][x]then y=i;goto L1 end end goto L2 ::L1:: if y then x=P.curX+x-1 for y=y0+y,#P.field do --Roof=finesse if P:solid(x,y)then finesse=true goto L2 end end end end else finesse=true end ::L2:: --Remove rows need to be cleared if cc>0 then for i=cc,1,-1 do _=P.clearedRow[i] freeRow.discard(rem(P.field,_)) freeRow.discard(rem(P.visTime,_)) if _<=P.garbageBeneath then P.garbageBeneath=P.garbageBeneath-1 gbcc=gbcc+1 end end end --Cancel no-sense clearing FX _=#P.clearingRow while _>0 and P.clearingRow[_]>#P.field do P.clearingRow[_]=nil _=_-1 end if P.clearingRow[1]then P.falling=P.gameEnv.fall elseif cc==P.r then clear=true end --Finesse check (control) if not finesse then if dospin then P.ctrlCount=P.ctrlCount-2 end--Allow 2 more step for roof-less spin local id=P.cur.id local d=P.ctrlCount-finesseList[id][P.dir+1][P.curX] if d>=2 then P:finesseError(2) elseif d>0 then P:finesseError(d) end end if cc>0 then cmb=cmb+1 if dospin then cscore=(spinSCR[P.cur.name]or spinSCR[8])[cc] if P.b2b>1000 then P:showText(text.b3b..text.block[P.cur.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 P.human then VOC.play("b3b",CHN) end elseif P.b2b>=50 then P:showText(text.b2b..text.block[P.cur.name]..text.spin.." "..text.clear[cc],0,-30,35,"spin") atk=b2bATK[cc] cscore=cscore*1.2 STAT.b2b=STAT.b2b+1 if P.human then VOC.play("b2b",CHN) end else P:showText(text.block[P.cur.name]..text.spin.." "..text.clear[cc],0,-30,45,"spin") atk=2*cc end sendTime=20+atk*20 if mini then P:showText(text.mini,0,-80,35,"appear") atk=atk*.25 sendTime=sendTime+60 cscore=cscore*.6 P.b2b=P.b2b+b2bPoint[cc]*.5 if P.human then VOC.play("mini",CHN) end else P.b2b=P.b2b+b2bPoint[cc] end P.lastClear=P.cur.id*10+cc if P.human then SFX.play(spin_n[cc]) VOC.play(spinName[P.cur.name],CHN) end elseif cc>=4 then cscore=cc==4 and 1000 or 1500 if P.b2b>1000 then P:showText(text.b3b..text.clear[cc],0,-30,50,"fly") atk=cc+2 sendTime=100 exblock=exblock+1 cscore=cscore*1.8 STAT.b3b=STAT.b3b+1 if P.human then VOC.play("b3b",CHN) end elseif P.b2b>=50 then P:showText(text.b2b..text.clear[cc],0,-30,50,"drive") sendTime=80 atk=cc+1 cscore=cscore*1.3 STAT.b2b=STAT.b2b+1 if P.human then VOC.play("b2b",CHN) end else P:showText(text.clear[cc],0,-30,70,"stretch") sendTime=60 atk=cc end P.b2b=P.b2b+cc*80-220 P.lastClear=P.cur.name*10+cc end if P.human then VOC.play(clearName[cc],CHN) end if clear then if #P.field==0 then P:showText(text.PC,0,-80,50,"flicker") atk=atk*.5+min(6+STAT.pc,12) exblock=exblock+2 sendTime=sendTime+120 if STAT.row+cc>4 then P.b2b=1200 cscore=cscore+300*min(6+STAT.pc,10) else cscore=cscore+626 end STAT.pc=STAT.pc+1 if P.human then SFX.play("clear") VOC.play("pc",CHN) end elseif cc>1 or #P.field==P.garbageBeneath then P:showText(text.HPC,0,-80,50,"fly") atk=atk+2 exblock=exblock+2 sendTime=sendTime+60 cscore=cscore+626 STAT.hpc=STAT.hpc+1 if P.human then SFX.play("clear") end else goto checkB2Breduce end P.lastClear=P.cur.name*10+5 goto skipB2Breduce end ::checkB2Breduce:: if not(dospin or cc>3)then P.b2b=max(P.b2b-250,0) P:showText(text.clear[cc],0,-30,27+cc*3,"appear",(8-cc)*.3) atk=cc-.5 sendTime=20+atk*20 cscore=cscore+clearSCR[cc] P.lastClear=cc end ::skipB2Breduce:: sendTime=sendTime+25*cmb if cmb>1 then atk=atk*(.8+.2*min(cmb,11)) if cmb>=3 then atk=atk+1 end P:showText(text.cmb[min(cmb,21)],0,25,15+min(cmb,25)*3,cmb<10 and"appear"or"flicker") cscore=cscore+min(50*cmb,500)*(2*cc-1) end if P.b2b>1200 then P.b2b=1200 end --Bonus atk/def when focused if modeEnv.royaleMode then local i=min(#P.atker,9) if i>1 then atk=atk+reAtk[i] exblock=exblock+reDef[i] end end send=atk if send>0 then if exblock>0 then exblock=int(exblock*(1+P.strength*.25))--Badge Buff P:showText(exblock,0,120,20,"zoomout") off=off+P:cancel(exblock) end send=int(send*(1+P.strength*.25))--Badge Buff if send>0 then P:showText(send,0,80,35,"zoomout") _=P:cancel(send) send=send-_ off=off+_ if send>0 then local T if modeEnv.royaleMode then if P.atkMode==4 then local M=#P.atker if M>0 then for i=1,M do P:attack(P.atker[i],send,sendTime,M,P.cur.color,P.lastClear,dospin,cmb) end else T=randomTarget(P) end else P:freshTarget() T=P.atking end elseif #players.alive>1 then T=randomTarget(P) end if T then P:attack(T,send,sendTime,1,P.cur.color,P.lastClear,dospin,cmb) end end if P.human and send>3 then SFX.play("emit",min(send,7)*.1)end end end if P.human then SFX.play(clear_n[cc]) SFX.play(ren_n[min(cmb,11)]) if cmb>14 then SFX.play("ren_mega",(cmb-10)*.1)end VIB(cc+1) end else cmb=0 local dropScore=10 --Spin bonus if dospin then P:showText(text.block[P.cur.name]..text.spin,0,-30,45,"appear") P.b2b=P.b2b+20 if P.human then SFX.play("spin_0") VOC.play(spinName[P.cur.name],CHN) end dropScore=25 end --DropSpeed bonus if P.gameEnv._20G then dropScore=dropScore*2 elseif P.gameEnv.drop<3 then dropScore=dropScore*1.5 end --Speed bonus if P.dropSpeed>60 then dropScore=dropScore*P.dropSpeed/60 elseif P.dropSpeed>120 then dropScore=dropScore*1.2*P.dropSpeed/120 elseif P.dropSpeed>180 then dropScore=dropScore*1.5*P.dropSpeed/180 end cscore=cscore+dropScore if P.b2b>1000 then P.b2b=max(P.b2b-40,1000) end P:garbageRelease() end P.combo=cmb STAT.score=STAT.score+int(cscore) STAT.piece=STAT.piece+1 STAT.row=STAT.row+cc 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 STAT.digatk=STAT.digatk+atk*gbcc/cc end --Update stat local n=P.cur.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 --Drop event _=P.gameEnv.dropPiece if _ then _(P)end --Stereo SFX if P.human then SFX.fieldPlay("lock",nil,P)end end ---------------------------------------------------- ---------------------------------------------------- local AM={} function AM.pressKey(P,i) P.keyPressing[i]=true P.act[i](P) if P.control then if P.keyRec then ins(P.keyTime,1,game.frame) P.keyTime[11]=nil end P.stat.key=P.stat.key+1 end end function AM.releaseKey(P,i) P.keyPressing[i]=false end function AM.pressKey_Rec(P,i) if game.recording then ins(game.rec,game.frame) ins(game.rec,i) end P.keyPressing[i]=true P.act[i](P) if P.control then if P.keyRec then ins(P.keyTime,1,game.frame) P.keyTime[11]=nil end P.stat.key=P.stat.key+1 end end function AM.releaseKey_Rec(P,i) if game.recording then ins(game.rec,game.frame) ins(game.rec,-i) end P.keyPressing[i]=false end ---------------------------------------------------- ---------------------------------------------------- local function gameOver()--Save record if game.replaying then return end FILE.saveData() local M=curMode local R=M.getRank if R then local P=players[1] R=R(P)--New rank if R then local r=modeRanks[M.name]--Old rank local _ if R>r then modeRanks[M.name]=R _=true end for i=1,#M.unlock do local m=M.unlock[i] local n=Modes[m].name if not modeRanks[n]then modeRanks[n]=Modes[m].score and 0 or 6 _=true end end if _ then FILE.saveUnlock() end local D=M.score(P) local L=M.records local p=#L--Rank-1 if p>0 then while M.comp(D,L[p])do--If higher rank p=p-1 if p==0 then break end end end if p<10 then if p==0 then P:showTextF(text.newRecord,0,-100,100,"beat",.5) end D.date=os.date("%Y/%m/%d %H:%M") ins(L,p+1,D) if L[11]then L[11]=nil end FILE.saveRecord(M.name,L) end end end end function player.die(P)--Called when win/lose,not really die! P.alive=false P.timing=false P.control=false P.update=Pupdate_dead P.waiting=1e99 P.b2b=0 TASK.clear(P) for i=1,#P.atkBuffer do P.atkBuffer[i].sent=true P.atkBuffer[i].time=0 end for i=1,#P.field do for j=1,10 do P.visTime[i][j]=min(P.visTime[i][j],20) end end end function player.win(P,result) P:die() P.result="WIN" if modeEnv.royaleMode then P.modeData.event=1 P:changeAtk() end if P.human then game.result=result or"win" SFX.play("win") VOC.play("win") if modeEnv.royaleMode then BGM.play("8-bit happiness") end end if curMode.id=="custom_puzzle"then P:showTextF(text.win,0,0,90,"beat",.4) else P:showTextF(text.win,0,0,90,"beat",.5,.2) end if P.human then gameOver() TASK.new(TICK.autoPause,{0}) if marking then P:showTextF(text.marking,0,-226,22,"appear",.4,.0626) end end P:newTask(TICK.finish) end function player.lose(P) if P.life>0 then for _=#P.field,1,-1 do freeRow.discard(P.field[_]) freeRow.discard(P.visTime[_]) P.field[_],P.visTime[_]=nil end if P.AI_mode=="CC"then CC_updateField(P) end P.life=P.life-1 return end P:die() for i=1,#players.alive do if players.alive[i]==P then rem(players.alive,i) break end end P.result="K.O." if modeEnv.royaleMode then P:changeAtk() P.modeData.event=#players.alive+1 P.strength=0 if P.lastRecv then local A,i=P,0 repeat A,i=A.lastRecv,i+1 until not A or A.alive or A==P or i==3 if A and A.alive then if P.id==1 or A.id==1 then P.killMark=A.id==1 end A.modeData.point,A.badge=A.modeData.point+1,A.badge+P.badge+1 for i=A.strength+1,4 do if A.badge>=royaleData.powerUp[i]then A.strength=i A.frameColor=frameColor[A.strength] end end P.lastRecv=A if P.id==1 or A.id==1 then TASK.new(TICK.throwBadge,{A.ai,P,max(3,P.badge)*4}) end freshMostBadge() end else P.badge=-1 end freshMostDangerous() for i=1,#players.alive do if players.alive[i].atking==P then players.alive[i]:freshTarget() end end if #players.alive==royaleData.stage[game.stage]then royaleLevelup() end P:showTextF(P.modeData.event,0,120,60,"appear",.26,.9) end P.gameEnv.keepVisible=P.gameEnv.visible~="show" P:showTextF(text.gameover,0,0,60,"appear",.26,.9) if P.human then game.result="gameover" SFX.play("fail") VOC.play("lose") if modeEnv.royaleMode then if P.modeData.event==2 then BGM.play("hay what kind of feeling") else BGM.play("end") end end gameOver() P:newTask(#players>1 and TICK.lose or TICK.finish) TASK.new(TICK.autoPause,{0}) if marking then P:showTextF(text.marking,0,-226,22,"appear",.4,.0626) end else P:newTask(TICK.lose) end if #players.alive==1 then players.alive[1]:win() end end function PLY.reach_winCheck(P) if P.stat.row>=P.gameEnv.target then P:win("finish") end end --------------------------<\Events>-------------------------- ---------------------------------------------------- player.act={} function player.act.moveLeft(P,auto) if not auto then P.ctrlCount=P.ctrlCount+1 end P.movDir=-1 if P.keyPressing[9]then if P.gameEnv.swap then P:changeAtkMode(1) P.keyPressing[1]=false end elseif P.control and P.waiting==-1 then if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY)then P.curX=P.curX-1 P:freshBlock(false,true) if P.human and P.curY==P.imgY then SFX.play("move")end if not auto then P.moving=0 end P.spinLast=false else P.moving=P.gameEnv.das end else P.moving=0 end end function player.act.moveRight(P,auto) if not auto then P.ctrlCount=P.ctrlCount+1 end P.movDir=1 if P.keyPressing[9]then if P.gameEnv.swap then P:changeAtkMode(2) P.keyPressing[2]=false end elseif P.control and P.waiting==-1 then if P.cur and not P:ifoverlap(P.cur.bk,P.curX+1,P.curY)then P.curX=P.curX+1 P:freshBlock(false,true) if P.human and P.curY==P.imgY then SFX.play("move")end if not auto then P.moving=0 end P.spinLast=false else P.moving=P.gameEnv.das end else P.moving=0 end end function player.act.rotRight(P) if P.control and P.waiting==-1 and P.cur then P.ctrlCount=P.ctrlCount+1 P:spin(1) P.keyPressing[3]=false end end function player.act.rotLeft(P) if P.control and P.waiting==-1 and P.cur then P.ctrlCount=P.ctrlCount+1 P:spin(3) P.keyPressing[4]=false end end function player.act.rot180(P) if P.control and P.waiting==-1 and P.cur then P.ctrlCount=P.ctrlCount+2 P:spin(2) P.keyPressing[5]=false end end function player.act.hardDrop(P) if P.keyPressing[9]then if P.gameEnv.swap then P:changeAtkMode(3) end P.keyPressing[6]=false elseif P.control and P.waiting<=0 and P.cur then if P.curY~=P.imgY then if P.gameEnv.dropFX then P:createDropFX(P.curX,P.curY+1,P.curX+P.c-1,P.imgY+P.r-1) end P.curY=P.imgY P.spinLast=false if P.gameEnv.shakeFX then P.fieldOff.vy=P.gameEnv.shakeFX*.6 end if P.human then SFX.fieldPlay("drop",nil,P) VIB(1) end end P.lockDelay=-1 P:drop() P.keyPressing[6]=false end end function player.act.softDrop(P) if P.keyPressing[9]then if P.gameEnv.swap then P:changeAtkMode(4) end else P.downing=1 if P.control and P.waiting==-1 and P.cur then if P.curY~=P.imgY then P.curY=P.curY-1 P:freshBlock(true,true) P.spinLast=false end end end end function player.act.hold(P) if P.control and P.waiting==-1 then P:hold() end end function player.act.func(P) P.gameEnv.Fkey(P) end function player.act.restart(P) if game.frame<240 or game.result then TASK.removeTask_code(TICK.autoPause) resetPartGameData() end end function player.act.insLeft(P,auto) if P.gameEnv.nofly or not P.cur then return end local x0=P.curX while not P:ifoverlap(P.cur.bk,P.curX-1,P.curY)do P.curX=P.curX-1 if P.gameEnv.dropFX then P:createDropFX(P.curX+P.c,P.curY+P.r-1,P.curX+P.c,P.curY) end P:freshBlock(false,true) end if P.curX~=x0 then P.spinLast=false end if P.gameEnv.shakeFX then P.fieldOff.vx=-P.gameEnv.shakeFX*.5 end if auto then if P.ctrlCount==0 then P.ctrlCount=1 end else P.ctrlCount=P.ctrlCount+1 end end function player.act.insRight(P,auto) if P.gameEnv.nofly or not P.cur then return end local x0=P.curX while not P:ifoverlap(P.cur.bk,P.curX+1,P.curY)do P.curX=P.curX+1 if P.gameEnv.dropFX then P:createDropFX(P.curX-1,P.curY+P.r-1,P.curX-1,P.curY) end P:freshBlock(false,true) end if P.curX~=x0 then P.spinLast=false end if P.gameEnv.shakeFX then P.fieldOff.vx=P.gameEnv.shakeFX*.5 end if auto then if P.ctrlCount==0 then P.ctrlCount=1 end else P.ctrlCount=P.ctrlCount+1 end end function player.act.insDown(P) if P.curY~=P.imgY and P.cur then if P.gameEnv.dropFX then P:createDropFX(P.curX,P.curY+1,P.curX+P.c-1,P.imgY+P.r-1) end if P.gameEnv.shakeFX then P.fieldOff.vy=P.gameEnv.shakeFX*.5 end P.curY,P.lockDelay,P.spinLast=P.imgY,P.gameEnv.lock,false P:freshBlock(true,true) end end function player.act.down1(P) if P.curY~=P.imgY and P.cur then P.curY=P.curY-1 P:freshBlock(true,true) P.spinLast=false end end function player.act.down4(P) for _=1,4 do if P.curY~=P.imgY and P.cur then P.curY=P.curY-1 P:freshBlock(true,true) P.spinLast=false else return end end end function player.act.down10(P) if P.cur then for _=1,10 do if P.curY~=P.imgY then P.curY=P.curY-1 P:freshBlock(true,true) P.spinLast=false else return end end end end function player.act.dropLeft(P) if P.gameEnv.nofly or not P.cur then return end P.act.insLeft(P) P.act.hardDrop(P) end function player.act.dropRight(P) if P.gameEnv.nofly or not P.cur then return end P.act.insRight(P) P.act.hardDrop(P) end function player.act.zangiLeft(P) if P.gameEnv.nofly or not P.cur then return end P.act.insLeft(P) P.act.insDown(P) P.act.insRight(P) P.act.hardDrop(P) end function player.act.zangiRight(P) if P.gameEnv.nofly or not P.cur then return end P.act.insRight(P) P.act.insDown(P) P.act.insLeft(P) P.act.hardDrop(P) end --Give operations a num name A=player.act T={ A.moveLeft ,A.moveRight, A.rotRight, A.rotLeft, A.rot180 ,A.hardDrop, A.softDrop, A.hold, A.func ,A.restart, A.insLeft, A.insRight, A.insDown ,A.down1, A.down4, A.down10, A.dropLeft ,A.dropRight, A.zangiLeft,A.zangiRight }for i=1,20 do A[i]=T[i]end;A,T=nil ---------------------------------------------------- ---------------------------------------------------- local function newEmptyPlayer(id,x,y,size) local P={id=id} players[id]=P players.alive[id]=P --Inherit functions of player class for k,v in next,player do P[k]=v end if P.id==1 and game.recording then P.pressKey=AM.pressKey_Rec P.releaseKey=AM.releaseKey_Rec else P.pressKey=AM.pressKey P.releaseKey=AM.releaseKey end P.update=Pupdate_alive P.fieldOff={x=0,y=0,vx=0,vy=0}--For shake FX P.x,P.y,P.size=x,y,size or 1 P.frameColor=frameColor[0] P.small=P.size<.1--If draw in small mode if P.small then P.centerX,P.centerY=P.x+300*P.size,P.y+600*P.size P.canvas=love.graphics.newCanvas(60,120) P.frameWait=rnd(30,120) P.draw=Pdraw_small else P.keyRec=true--If calculate keySpeed P.centerX,P.centerY=P.x+300*P.size,P.y+370*P.size P.absFieldX=P.x+150*P.size P.absFieldY=P.y+60*P.size P.draw=Pdraw_norm P.bonus={}--Text objects end P.randGen=mt.newRandomGenerator(game.seed) P.small=false P.life=0 P.alive=true P.control=false P.timing=false P.stat=getNewStatTable() P.modeData={point=0,event=0,counter=0}--Data use by mode P.keyTime={}P.keySpeed=0 P.dropTime={}P.dropSpeed=0 for i=1,10 do P.keyTime[i]=-1e5 end for i=1,10 do P.dropTime[i]=-1e5 end P.field,P.visTime={},{} P.atkBuffer={sum=0} --Royale-related P.badge,P.strength=0,0 P.atkMode,P.swappingAtkMode=1,20 P.atker,P.atking,P.lastRecv={} P.dropDelay,P.lockDelay=0,0 P.color={} P.showTime=nil P.keepVisible=true --P.cur={bk=matrix[2], id=shapeID, color=colorID, name=nameID} --P.sc,P.dir={0,0},0--SpinCenterCoord, direction --P.r,P.c=0,0--row, col --P.hd={...},same as P.cur -- P.curX,P.curY,P.imgY,P.minY=0,0,0,0--x,y,ghostY P.holded=false P.next={} P.freshTime=0 P.spinLast,P.lastClear=false,nil P.spinSeq=0--For Ospin, each digit mean a spin P.ctrlCount=0--Key press time, for finesse check P.pieceCount=0--Count pieces from next, for drawing bagline P.human=false P.RS=kickList.TRS -- P.newNext=nil--Call prepareSequence()to get a function to get new next P.keyPressing={}for i=1,12 do P.keyPressing[i]=false end P.movDir,P.moving,P.downing=0,0,0--Last move key,DAS charging,downDAS charging P.waiting,P.falling=-1,-1 P.clearingRow,P.clearedRow={},{}--Clearing animation height,cleared row mark P.combo,P.b2b=0,0 P.garbageBeneath=0 P.fieldBeneath=0 P.score1,P.b2b1=0,0 P.dropFX,P.lockFX,P.clearFX={},{},{} P.tasks={}--Tasks P.bonus={}--Texts P.endCounter=0--Used after gameover P.result=nil--String:"WIN"/"K.O." return P end local function loadGameEnv(P)--Load gameEnv P.gameEnv={}--Current game setting environment local ENV=P.gameEnv local E --Load game settings for k,v in next,gameEnv0 do if modeEnv[k]~=nil then v=modeEnv[k] --Mode setting -- DBP("mode-"..k..":"..tostring(v)) elseif game.setting[k]~=nil then v=game.setting[k] --Game setting -- DBP("game-"..k..":"..tostring(v)) elseif setting[k]~=nil then v=setting[k] --Global setting -- DBP("global-"..k..":"..tostring(v)) -- else -- DBP("default-"..k..":"..tostring(v)) end ENV[k]=v --Default setting end end local function applyGameEnv(P)--Finish gameEnv processing local ENV=P.gameEnv P.dropDelay=ENV.drop P.lockDelay=ENV.lock P.color={} for _=1,7 do P.color[_]=SKIN.libColor[ENV.skin[_]] end P.keepVisible=ENV.visible=="show" P.showTime= ENV.visible=="show"and 1e99 or ENV.visible=="time"and 300 or ENV.visible=="fast"and 20 or ENV.visible=="none"and 0 ENV.das=max(ENV.das,ENV.mindas) ENV.arr=max(ENV.arr,ENV.minarr) ENV.sdarr=max(ENV.sdarr,ENV.minsdarr) ENV.next=min(ENV.next,setting.maxNext) if ENV.sequence~="bag"and ENV.sequence~="loop"then ENV.bagLine=false else ENV.bagLen=#ENV.bag end if ENV.lockFX==0 then ENV.lockFX=nil end if ENV.dropFX==0 then ENV.dropFX=nil end if ENV.clearFX==0 then ENV.clearFX=nil end if ENV.shakeFX==0 then ENV.shakeFX=nil end end local function prepareSequence(P)--Call freshPrepare and set newNext local ENV=P.gameEnv if type(ENV.sequence)=="string"then freshPrepare[ENV.sequence](P) P.newNext=freshMethod[ENV.sequence] else assert(type(ENV.sequence)=="function"and type(ENV.freshMethod)=="function","wrong sequence generator code") ENV.sequence(P) P.newNext=ENV.freshMethod end end local function loadAI(P,AIdata)--Load AI params local ENV=P.gameEnv P.AI_mode=AIdata.type P.AI_stage=1 P.AI_keys={} P.AI_delay=AIdata.delay or min(int(ENV.drop*.8),2*AIdata.delta) P.AI_delay0=AIdata.delta P.AIdata={ next=AIdata.next, hold=AIdata.hold, _20G=ENV._20G, bag=AIdata.bag=="bag", node=AIdata.node, } if not BOT then P.AI_mode="9S"end if P.AI_mode=="CC"then P.RS=kickList.AIRS local opt,wei=BOT.getConf() BOT.setHold(opt,P.AIdata.hold) BOT.set20G(opt,P.AIdata._20G) BOT.setBag(opt,P.AIdata.bag) BOT.setNode(opt,P.AIdata.node) P.AI_bot=BOT.new(opt,wei) BOT.free(opt)BOT.free(wei) for i=1,AIdata.next do BOT.addNext(P.AI_bot,CCblockID[P.next[i].id]) end elseif P.AI_mode=="9S"then P.RS=kickList.TRS end end function PLY.newDemoPlayer(id,x,y,size) local P=newEmptyPlayer(id,x,y,size) P.life=1e99 -- rewrite draw arguments P.small=false P.keyRec=false P.centerX,P.centerY=P.x+300*P.size,P.y+600*P.size P.absFieldX=P.x+150*P.size P.absFieldY=P.y+60*P.size P.draw=Pdraw_demo P.update=Pupdate_alive P.control=true P.atker={}P.strength=0 P.field,P.visTime={},{} P.atkBuffer={sum=0} P.gameEnv={ noFly=false, das=10,arr=2, sddas=2,sdarr=2, swap=true, ghost=setting.ghost,center=setting.center, smooth=setting.smooth,grid=setting.grid, text=setting.text, lockFX=setting.lockFX,dropFX=setting.dropFX, clearFX=setting.clearFX,shakeFX=setting.shakeFX, _20G=false,bone=false, drop=1e99,lock=1e99, wait=10,fall=20, next=6,hold=true, oncehold=true, ospin=true, sequence="bag", bag={1,2,3,4,5,6,7}, face={0,0,0,0,0,0,0}, skin=setting.skin, pushSpeed=3, block=true, visible="show", Fkey=nil,puzzle=false, freshLimit=1e99,easyFresh=true, fine=false,fineKill=false, target=1e99,dropPiece=NULL, mindas=0,minarr=0,minsdarr=0, } applyGameEnv(P) P.dropDelay,P.lockDelay=1e99,1e99 P.showTime=1e99 P.keepVisible=true --Always use "bag" freshPrepare.bag(P) P.newNext=freshMethod.bag P.human=false loadAI(P,{ type="CC", next=5, hold=true, delay=3, delta=3, bag="bag", node=80000, }) P:popNext() end function PLY.newRemotePlayer(id,x,y,size,actions) local P=newEmptyPlayer(id,x,y,size) P.human=false P.remote=true -- P.updateAction=buildActionFunctionFromActions(P, actions) loadGameEnv(P) applyGameEnv(P) prepareSequence(P) end function PLY.newAIPlayer(id,x,y,size,AIdata) local P=newEmptyPlayer(id,x,y,size) if P.small then ENV.text=false ENV.lockFX=nil ENV.dropFX=nil ENV.shakeFX=nil end loadGameEnv(P) applyGameEnv(P) local ENV=P.gameEnv ENV.face={0,0,0,0,0,0,0} ENV.skin={1,5,8,2,10,3,7} prepareSequence(P) P.human=false loadAI(P,AIdata) end function PLY.newPlayer(id,x,y,size) local P=newEmptyPlayer(id,x,y,size) loadGameEnv(P) applyGameEnv(P) prepareSequence(P) P.human=true P.RS=kickList.TRS end ---------------------------------------------------- return PLY