0,]']>S}Wup8dH)6o;]AsVl3'MMt'mmm G'mmmm t'mmmmmmmw'mmmmmmm(M M M M M M Mu'mmmmmmm p0m m m m mm m mm mM mM mm mM mm mM m``````````````````````````8'l l;'l l Ll L l l llLll L Ll L l l llLll l l l ll ll ll ll ll l  l Mm M m m mmMmm M Mm M m l mmMmm m m m mm mm mm mm mm m  m`````````````````mmmm m m m m mmmmmmmmmmmmm m m m m m mm m m m m m m mmmmmmm m m mm mmmm m pp p pp p pp p pp p p p ppp ppp pp p p p p ppPpppppPppppppPpppppPpppp 0 000@((1p599 = =`aeeduuuuuu ``p PP(@@@@ОТЦ00PP<?           ""!" 33 "!! 32  !! ##   ""  ""  """ 3323#33  "! 322##32  ! ##22"##  """""""   """""""                                             UU   UV   ee   ff  ff  UUVUe  UVVee   eeVVf  fffff  fffff  PUe  Pee  PVf  `ff  `ff                               =3#  =##  =2"  -""  -""   =3# DDCD4 =## DCC44  =2" 44CC3 -"" 33333  -""33333  =3#33RUeUU&"DDCD4  =##32ReeUV&DCC44  =2"##RVfee&!44CC3  -""""bffff33333 -""""bffff33333 ݝUUVUe""!"xw  ݝUVVee"!!x ݝeeVVf!!xq   ݍfffff  ݍfffff ݝ""qwwwqwwwqw ݝ"!qwqqwqq  ݝqqqqqq  ݍ ݍ  =3#33BD4DDww!"""qw =##32B44DCwq!"!q =2"##BC344!!qq -""""23333 -""""23333 ݽ33BD4DD""!"  ݽ32B44DC"!!  ݽ##BC344!!  ݭ""23333 ݭ""23333      /} ll l l ll ll l lll ll l l lll l l l ll``````````````````` l l ll l l l l lll l l l ll l ll ll l l l l ll l l l l lll l l l ll l ll ll llm mm m mm mm mm m mm m m mmmM mm mm mm mm m m p p p p p p p p p p p p p p p p p p p p p p p p pp p p p pbbbb b bbbBb bb bbb Bb bB bbbbb````````````````````` bb bb bb bbbbbbBbb Bbb bb bb bb bbbbbbBbb B.   <pp ,,48;-- title: Master of Blocks 5-X: Devastation -- author: Phoenix Flare -- desc: Go for top rank one last time... -- script: lua -- input: gamepad -- saveid: MoB Devastation -- Copyright 2021-2022 Phoenix Flare -- 5-X Revision 2D function loadcfg() local c=pmem(200) local ver=pmem(255) if c&0x80==0 or ver<100 then cfg={ timer=false, joe=false, pause=true, bga=true, free=true, coin=0, vol={ snd=15, bgm=15, }, } pmem(200, 0xFFCC) pmem(255, 100) initHSTable() return end cfg={ timer=(c&0x1)==1, joe=(c&0x2)==2, pause=(c&0x4)==4, bga=(c&0x8)==8, free=(c&0x40)==0x40, coin=(c&0x30)>>4, vol={ snd=(c&0xF00)>>8, bgm=(c&0xF000)>>12, }, } end function savecfg() pmem(200, (cfg.vol.bgm<<12)+ (cfg.vol.snd<<8)+ 0x80+ (cfg.free and 0x40 or 0)+ (cfg.coin<<4)+ (cfg.bga and 0x8 or 0)+ (cfg.pause and 0x4 or 0)+ (cfg.joe and 0x2 or 0)+ (cfg.timer and 0x1 or 0) ) end function initHSTable() local table={ {[0]=45, 500, 15*60*60, 0x0D0F02, 0, 3}, {[0]=36, 400, 15*60*60, 0x0D0F02, 0, 2}, {[0]=27, 300, 15*60*60, 0x0D0F02, 0, 1}, } for x=0,5 do for y=1,3 do for z=0,5 do if x==4 and z==3 then pmem(x*30+y*10+z-10, 0x093A3B) elseif z==4 then pmem(x*30+y*10+z-10, math.random(3)-1) elseif x==5 and z==2 then pmem(x*30+y*10+z-10, 999) else pmem(x*30+y*10+z-10, table[y][z]) end end end end end function loadHSTable() highScore={} highScore.all={} highScore.today={} for spec=0,5 do highScore.all[spec]={} if spec==4 then highScore.today[spec]={ {[0]=27, 300, 15*60*60, 0x093A3B, math.random(3)-1, 2}, {[0]=18, 200, 15*60*60, 0x093A3B, math.random(3)-1, 1}, {[0]=9, 100, 15*60*60, 0x093A3B, math.random(3)-1, 0}, } elseif spec==5 then highScore.today[spec]={ {[0]=27, 300, 999, 0x0D0F02, math.random(3)-1, 2}, {[0]=18, 200, 999, 0x0D0F02, math.random(3)-1, 1}, {[0]=9, 100, 999, 0x0D0F02, math.random(3)-1, 0}, } else highScore.today[spec]={ {[0]=27, 300, 15*60*60, 0x0D0F02, math.random(3)-1, 2}, {[0]=18, 200, 15*60*60, 0x0D0F02, math.random(3)-1, 1}, {[0]=9, 100, 15*60*60, 0x0D0F02, math.random(3)-1, 0}, } end for pos=1,3 do highScore.all[spec][pos]={} for idx=0,5 do highScore.all[spec][pos][idx]=pmem(spec*30+pos*10+idx-10) end end end end function checkHSPosition(p) local hs={ highScore.all[p.special], highScore.today[p.special], } for t=1, 2 do for x=1, 3 do if p.grade>hs[t][x][0] then p.place[t]=x break elseif p.grade==hs[t][x][0] then if p.level>hs[t][x][1] then p.place[t]=x break elseif p.level==hs[t][x][1] then if (p.special==5 and p.blocksUsed or p.timer)14 and #rnd.bag or #rnd.bag+1) if piece>#rnd.bag then initBag(rnd) return randomDraw(pid) end drawn=true for x=1,4 do if rnd.bag[piece]==rnd.history[x] then drawn=false end end rerolls=rerolls-1 if rerolls==0 or drawn then table.remove(rnd.history, 1) rnd.history[4]=table.remove(rnd.bag, piece) return rnd.history[4] end end end function initQueue(pid) player[pid].next={} for x=1,3 do player[pid].next[x]=randomDraw(pid)+player[pid].rule*7 end end function initGame(pid) initGrid(pid) local p=player[pid] --Block Data initRandomizer(pid) initQueue(pid) p.currentBlock=nil p.heldBlock=nil p.held=false p.slow=0 p.slowActive=false p.amulet=0 p.amuletActive=0 p.emergencyAmuletTimer=0 p.are=30 p.lcf=0 p.ldas=0 p.rdas=0 --UI elements p.timer=0 p.score=0 p.level=0 p.grade=0 p.rollLines=0 p.canIntoTopRank=false --Scoring p.combo=1 p.markedLines={} --Cools p.cools=0 p.ctimer=0 p.cquads=0 p.cspins=0 --Music p.track=-1 end player={} cheats={{}, {}} initPlayer(1) initPlayer(2) menu=-1 idle=600 idle2=0 credits=0 splash=0 function TIC() vbank(0) if splash<315 then cls() vbank(1) cls() vbank(0) splash=splash+1 local fade=math.max(math.min(math.min(splash, 300-splash), 45), 0) poke(0x3FD2, 0x1E*fade/45+0x1A) poke(0x3FD3, 0x9B*fade/45+0x1C) poke(0x3FD4, 0x38*fade/45+0x2C) spr(384, 86, 26, 8, 1, 0, 0, 8, 8) printCenter("GREEN ALPACA", 94, 6) printCenter("SOFT", 100, 6) if splash==315 then poke(0x3FD2, 0x38) poke(0x3FD3, 0xB7) poke(0x3FD4, 0x64) cls() end return end update() drawBackground() if idle<600 and menu==-1 then drawFieldBorder(player[1]) drawFieldBorder(player[2]) end vbank(1) cls(0) for x=0, 2 do poke(0x3FC0+1*3+x, ({0x5D, 0x27, 0x5D})[x+1]) poke(0x3FC0+2*3+x, ({0xB1, 0x3E, 0x53})[x+1]) poke(0x3FC0+3*3+x, ({0xEF, 0x7D, 0x57})[x+1]) poke(0x3FC0+12*3+x, 0xF4) poke(0x3FC0+14*3+x, ({0x56, 0x6C, 0x86})[x+1]) end draw() vbank(0) end function SCN(ln) local t=time() local l=0.5-math.abs((68-ln)/136) for x=0, 2 do poke(0x3FC0+1*3+x, math.floor(l*(math.sin(math.rad(t/18+120*x))+1)*0xFF)) if ln==0 then if (menu~=-1 or idle>=1200) then poke(0x3FC0+12*3+x, (0xCF*peek(0x3FC0+8*3+x))//0xFF) poke(0x3FC0+14*3+x, (0x7F*peek(0x3FC0+8*3+x))//0xFF) else poke(0x3FC0+12*3+x, basePalette[12][x+1]) poke(0x3FC0+14*3+x, basePalette[14][x+1]) end end end for x=1, 2 do local l if player[x].difficulty==4 then l=12 elseif player[x].state=="Versus" or player[x].state=="VersusStart" then l=11 else l=(player[x].level//100+player[x].cools//100)//math.min(4, player[x].difficulty+1)+1 end if l<10 then if ln==0 then local c={ {0x94, 0xB0, 0xC2}, {0x41, 0xA6, 0xF6}, {0xA7, 0xF0, 0x70}, {0xFF, 0xCD, 0x75}, {0xEF, 0x7D, 0x57}, {0xB1, 0x3E, 0x53}, {0xBA, 0x48, 0x91}, {0x1A, 0x1C, 0x2C}, {0xF4, 0xF4, 0xF4}, } for y=0, 2 do poke(0x3FC0+(x+1)*3+y, c[l][y+1]) end end elseif l==10 then if ln>=24 and ln<132 then for y=0, 2 do poke(0x3FC0+(x+1)*3+y, (math.sin(math.rad(180+ln-t/6))*0xFF+0xFF)//2) end end elseif l==11 then if ln>=24 and ln<132 then for y=0, 2 do poke(0x3FC0+(x+1)*3+y, (math.sin(math.rad(120*y+180+ln-t/6))*0xFF+0xFF)//2) end end elseif l==12 then if ln>=24 and ln<132 then poke(0x3FC0+(x+1)*3, math.random(255)) for y=1, 2 do poke(0x3FC0+(x+1)*3+y, 0) end end end end end --Update and Game Logic functions function update() for x=0,3 do local vol=x<2 and cfg.vol.snd or cfg.vol.bgm if peek(0xFF9D+18*x)&0xF0~=0 then poke(0xFF9D+18*x, (peek(0xFF9D+18*x)&0x0F)+((((peek(0xFF9D+18*x)&0xF0)*vol)//15)&0xF0)) end end if not cfg.free then if btnp(20) then credits=credits+({1, 2, 1})[cfg.coin+1] sfx(20, nil, -1, 2) if idle<600 then idle=0 else idle=600 end end if btnp(21) then credits=credits+({1, 2, 1})[cfg.coin+1] sfx(20, nil, -1, 2) if idle<600 then idle=0 else idle=600 end end end if menu~=-1 then return menuUpdate() end if idle>=600 then cheats={{}, {}} if btnp(6) and idle<1200 then idle=1200 end end if idle>=1200 then idle2=idle2+1 if btnp(2) then idle=idle-(idle%300)+(cfg.joe and 1500 or 1200) idle2=0 end if btnp(3) then idle=idle-(idle%300)+300 idle2=0 end end if player[1].state=="Idle" and player[2].state=="Idle" then if (btnp(7, 300, 0) and btnp(15, 300, 0)) or btnp(23) then menu=0 return end idle=idle+1 else idle=0 end if idle>=1500 then if btnp(4) or btnp(12) or idle2==(cfg.joe and 1800 or 1500) then idle=600 idle2=0 end return end for pid=1, 2 do local p=player[pid] local opponent=player[3-pid] local boffset=pid*8-8 if p.state=="Idle" then p.codeTime=p.codeTime+1 for x=0,7 do if btnp(x+boffset) then p.code=((p.code<<3)&0x7FFFFFF)+x p.codeTime=0 end end if p.codeTime==30 then p.code=4 end if p.code==0x124924D then idle=0 cheats[pid][1]=true end if p.code&0x7FFF==0x100D then idle=0 cheats[pid][2]=true end if p.code&0x3FFFF==0x1248D then idle=0 cheats[pid][3]=true end end if p.state=="Idle" then if (cfg.free or credits>=math.max(cfg.coin, 1)) and btnp(4+boffset) and opponent.state~="Config" and opponent.state~="Start" and opponent.state~="Win" then if not cfg.free then credits=credits-math.max(cfg.coin, 1) end initPlayer(pid, true) p=player[pid] p.cheat=cheats[pid] cheats[pid]={} p.state="Config" p.menu=-1 p.menutimer=900 end elseif p.state=="Config" then p.menutimer=p.menutimer-1 if btnp(2+boffset) or btnp(3+boffset) then if p.menu==-1 then p.special=(p.special+5)%10 p.denyversus=not p.denyversus elseif p.menu==0 then p.special=(p.special+(btnp(2+boffset) and -1 or 1))%(cfg.joe and 5 or 4) elseif p.menu==1 then p.reverse=not p.reverse elseif p.menu==2 then p.sonic=not p.sonic elseif p.menu==3 then p.denyversus=not p.denyversus end end if btnp(0+boffset) or btnp(1+boffset) then if p.menu==4 then p.rule=(p.rule+(btnp(0+boffset) and -1 or 1))%3 end if p.menu==5 then if btnp(1+boffset) then p.unlock=p.unlock+1 elseif btnp(0+boffset) then p.unlock=0 end p.difficulty=(p.difficulty+(btnp(0+boffset) and -1 or 1))%(p.unlock<13 and 4 or 5) end end if (p.menutimer==0 and cfg.timer) or btnp(4+boffset) then if p.menu==5 then p.state="Start" initGame(pid) p.menutimer=90 if p.special<5 and opponent.state=="InGame" and not (opponent.denyversus or p.denyversus) then p.state="Challenge" p.menutimer=600 p.menu=0 else sfx(15, nil, -1, pid-1) end else p.menu=p.menu+1 if (p.menu==3 or p.menu==0) and p.special==5 then p.menu=p.menu+1 end p.menutimer=900 end end elseif p.state=="Challenge" then p.menutimer=p.menutimer-1 if btnp(0+boffset) or btnp(1+boffset) then p.menu=(p.menu+1)%2 end if opponent.state=="Start" or opponent.state=="Curtain" then sfx(15, nil, -1, pid-1) p.state="Start" initGame(pid) p.menutimer=90 elseif (p.menutimer==0 and cfg.timer) or btnp(4+boffset) then p.state="Start" p.menutimer=90 if p.menu==1 then sfx(17, nil, -1, pid-1) music() playingMusic=false p.state="VersusWarn" p.menutimer=120 p.cheat=opponent.cheat opponent.state="VersusWarn" opponent.pause=false opponent.menutimer=120 else sfx(15, nil, -1, pid-1) end end elseif p.state=="Start" then p.lineData=nil p.menutimer=p.menutimer-1 p.versus=false if p.menutimer==0 then if p.difficulty<4 then p.track=0 if opponent.track==-1 then music(0) playingMusic=true end else p.track=20 if opponent.track<20 then sync(16, 1) music(0) playingMusic=true end end p.state="InGame" end elseif p.state=="InGame" then gameUpdate(p) elseif p.state=="RollStart" then p.menutimer=p.menutimer-1 p.versus=false if p.menutimer==0 then if not playingMusic then music(p.difficulty<4 and 6 or 2) playingMusic=true end p.rollTimer=2700 p.rollBlocks=100 initGrid(pid) p.state="Roll" end elseif p.state=="Roll" then gameUpdate(p) if (p.special==5 and p.rollBlocks or p.rollTimer)==0 then gameOver(p) p.canIntoTopRank=p.cools>=3000 and p.rollLines>=30 getGrade(p) end elseif p.state=="Curtain" then p.menutimer=p.menutimer+(btn(4+boffset) and 2 or 1) if p.menutimer>99 then if p.wins~=-1 and not (p.cheat[1] or p.cheat[2] or p.cheat[3]) then checkHSPosition(p) if p.place[2]<4 then if opponent.state=="NameEntry" and opponent.special==p.special then p.state="NameWait" return end p.state="NameEntry" p.menutimer=3600 p.menu=1 return end end p.state="GameOver" end elseif p.state=="VersusWarn" then p.menutimer=p.menutimer-1 if p.menutimer==0 then initGame(pid) p.versus=true p.state="VersusStart" p.menutimer=90 end elseif p.state=="VersusStart" then p.menutimer=p.menutimer-1 if p.menutimer==0 then p.state="Versus" music(7) playingMusic=true end elseif p.state=="Versus" then gameUpdate(p) elseif p.state=="Win" then p.menutimer=p.menutimer-1 if p.menutimer==0 then p.state="Start" p.menutimer=90 end elseif p.state=="NameWait" then if opponent.state=="NameEntry" then return end checkHSPosition(p) if p.place[2]<4 then p.state="NameEntry" p.menutimer=3600 p.menu=1 else p.state="GameOver" end elseif p.state=="NameEntry" then p.menutimer=p.menutimer-1 if cfg.timer and p.menu~=6 and p.menutimer==0 then p.menu=6 p.menutimer=300 end if p.menu==6 and p.menutimer==0 then p.state="GameOver" end if p.menu==6 then return end if btnp(2+boffset, 20, 4) then if p.menu<4 then p.name[p.menu]=(p.name[p.menu]-1)%62 else p.menu=(p.menu-3)%2+4 end elseif btnp(3+boffset, 20, 4) then if p.menu<4 then p.name[p.menu]=(p.name[p.menu]+1)%62 else p.menu=(p.menu-3)%2+4 end end if btnp(4+boffset) then if p.menu==4 then p.menu=6 p.menutimer=300 return saveName(p) elseif p.menu==5 then p.menu=3 else if p.name[p.menu]<60 then p.menu=p.menu+1 p.name[p.menu]=p.name[p.menu-1] end if p.name[p.menu]==61 then p.name[p.menu]=0 p.menu=6 p.menutimer=300 return saveName(p) end if p.name[p.menu]==60 then p.name[p.menu]=0 p.menu=math.max(1, p.menu-1) end end end if btnp(5+boffset) then p.name[p.menu]=0 p.menu=math.max(1, p.menu-1) end elseif p.state=="GameOver" then p.lineData=nil p.menutimer=p.menutimer+1 if p.menutimer==400 or btnp(4+boffset) then p.wins=0 p.state="Idle" if p.versus then p.versus=false initGame(pid) end end end end end function menuUpdate() if btnp(0) then menu=(menu-1)%10 end if btnp(1) then menu=(menu+1)%10 end if menu==0 then if btnp(2) or btnp(3) then cfg.vol.snd=math.max(0, math.min(15, cfg.vol.snd+(btnp(2) and -1 or 1))) end elseif menu==1 then if btnp(2) or btnp(3) then cfg.vol.bgm=math.max(0, math.min(15, cfg.vol.bgm+(btnp(2) and -1 or 1))) end elseif menu==2 then if btnp(4) then cfg.timer=not cfg.timer end elseif menu==3 then if btnp(4) then cfg.pause=not cfg.pause end elseif menu==4 then if btnp(4) then cfg.bga=not cfg.bga end elseif menu==5 then if btnp(4) then cfg.free=not cfg.free end elseif menu==6 then if btnp(2) or btnp(3) then cfg.coin=(cfg.coin+(btnp(2) and -1 or 1))%3 end elseif menu==9 then if btnp(4) then cfg.joe=not cfg.joe end end if menu==7 and btnp(4) or btnp(7) then savecfg() reset() end if menu==8 and btnp(4, 300, 0) and btnp(5, 300, 0) and not btnp(4) then pmem(200, 0) reset() end end function gameUpdate(p) for x=0, 209 do local b=p.gridAge[x//10][x%10+1] if b<0 then p.gridAge[x//10][x%10+1]=b+1 end end local boffset=p.id*8-8 if cfg.pause and btnp(7+boffset) then p.pause=not p.pause end if p.pause then if p.state=="Versus" then p.pause=false else return end end if btnp(6+boffset) and p.special==2 and (p.slow>=300 or p.slowActive) then p.slowActive=not p.slowActive end if btn(6+boffset) and p.amulet>4 and p.are<=0 then p.amuletActive=p.amulet//5 p.amulet=p.amulet%5 end if p.slow==0 then p.slowActive=false end if p.slowActive then p.slow=p.slow-1 else if p.state~="Roll" then p.timer=p.timer+1 p.ctimer=p.ctimer+1 else p.rollTimer=p.rollTimer-1 end end p.are=p.are-1 p.lcf=p.lcf-1 p.emergencyAmuletTimer=p.emergencyAmuletTimer-1 if p.lineData then p.lineData[#p.lineData]=p.lineData[#p.lineData]-1 if p.lineData[#p.lineData]==0 then p.lineData=nil end end if btn(2+boffset) then p.ldas=p.ldas+1 else p.ldas=0 end if btn(3+boffset) then p.rdas=p.rdas+1 else p.rdas=0 end if p.lcf==0 or p.emergencyAmuletTimer==0 then clearLines(p) if p.amuletActive>0 and p.emergencyAmuletTimer~=0 then amulet(p) end end if p.emergencyAmuletTimer>0 then return end if p.are==0 then if p.amuletActive>0 then clearLines(p) end spawnBlock(p) if p.level~=1000 and p.state~="Versus" then if p.timer>30 and (p.level%100<99 or p.state=="Versus") then p.level=p.level+1 checkCool(p) end if p.level%100==99 and p.state~="Versus" and not p.levelstopped then p.levelstopped=true sfx(9, nil, -1, p.id-1) end end end if p.currentBlock and p.state~="Curtain" then updateBlock(p) end end function spawnBlock(p) if p.special==5 then if p.blocks==0 then return gameOver(p) end p.blocks=p.blocks-1 end local boffset=p.id*8-8 local nextBlock=table.remove(p.next, 1) p.next[3]=randomDraw(p.id)+p.rule*7 local hold=p.special==1 and btn(6+boffset) local irot=(btn((p.reverse and 5 or 4)+boffset) and 3) or (btn((p.reverse and 4 or 5)+boffset) and 1) or ((btn(6+boffset) and p.special==0) and 2) or 0 if p.heldBlock or not hold then p.currentBlock={ block=(hold and p.heldBlock or nextBlock)+(p.cheat[3] and 21 or 0), rotation=irot, x=5, y=2, gravity=0, resetVal=pieceStartingLD*(p.state=="Versus" and 8 or 15), lifeVal=pieceStartingLD, dropped=0, age=0, idle=0, } local cb=p.currentBlock if not checkPosition(p, 0, 0) then cb.rotation=0 if not checkPosition(p, 0, 0) then cb.block=nextBlock+(p.cheat[3] and 21 or 0) hold=false cb.rotation=irot if not checkPosition(p, 0, 0) then cb.rotation=0 if not checkPosition(p, 0, 0) then return gameOver(p) end end end end if hold then p.heldBlock=nextBlock end else p.currentBlock={ block=p.next[1]+(p.cheat[3] and 21 or 0), rotation=irot, x=5, y=2, gravity=0, resetVal=pieceStartingLD*(p.state=="Versus" and 8 or 15), lifeVal=pieceStartingLD, dropped=0, age=0, idle=0, } if not checkPosition(p, 0, 0) then cb.rotation=0 if not checkPosition(p, 0, 0) then cb.block=nextBlock+(p.cheat[3] and 21 or 0) hold=false cb.rotation=irot if not checkPosition(p, 0, 0) then cb.rotation=0 if not checkPosition(p, 0, 0) then return gameOver(p) end end end end if hold then p.heldBlock=nextBlock table.remove(p.next, 1) p.next[3]=randomDraw(p.id)+p.rule*7 end end sfx((p.next[1]-1)%7+1, nil, -1, p.id-1) p.held=hold end function gameOver(p) if p.amulet>9 then p.amuletActive=p.amulet//10 p.amulet=0 return amulet(p, true) end o=player[3-p.id] p.state="Curtain" if o.track==-1 then sync() music() playingMusic=false elseif playingMusic and o.track1200 and cfg.timer and not cfg.pause then return lock(p) end if btnp(0+boffset) or btnp(1+boffset) or (p.level+(p.cools//100)*100)//(p.slowActive and 2 or 1)<(p.difficulty==4 and 0 or 500) then cb.unsafe=true end if not checkPosition(p, 0, 1) then cb.lifeVal=cb.lifeVal-(pieceStartingLD//getSpeedValue(p, "lockDelay")) if cb.lifeVal<1 or (p.sonic and btn(1+boffset) and (cb.unsafe or btnp(1+boffset))) then return lock(p) end end cb.gravity=cb.gravity+getSpeedValue(p, "gravity") if cb.gravity<256 and btn(1+boffset) then cb.gravity=256 end if btnp(0+boffset) then cb.gravity=5120 end while cb.gravity>=256 do if tryMove(p, 0, 1) and (btn(1+boffset) or btnp(0+boffset)) then cb.dropped=cb.dropped+1 if btnp(0+boffset) then cb.dropped=cb.dropped+1 end end cb.gravity=cb.gravity-256 end if btnp(0+boffset) and not p.sonic then return lock(p) end local dasKicked=false if cb.age>0 then if btnp((p.reverse and 5 or 4)+boffset) then dasKicked=rotate(p, -1) elseif btnp((p.reverse and 4 or 5)+boffset) then dasKicked=rotate(p, 1) elseif btnp(6+boffset) and p.special==0 then dasKicked=rotate(p, 2) elseif btnp(6+boffset) and p.special==1 and not p.held then local irot=(btn((p.reverse and 5 or 4)+boffset) and 3) or (btn((p.reverse and 4 or 5)+boffset) and 1) or ((btn(6+boffset) and not p.special==1) and 2) or 0 local held=p.heldBlock if not held then held=table.remove(p.next, 1) p.next[3]=randomDraw(p.id)+p.rule*7 end p.heldBlock=(p.currentBlock.block-1)%21+1 p.currentBlock={ block=held+(p.cheat[3] and 21 or 0), rotation=irot, x=5, y=2, gravity=0, resetVal=pieceStartingLD*(p.state=="Versus" and 8 or 15), lifeVal=pieceStartingLD, dropped=0, age=0, idle=0, } p.held=true if not checkPosition(p, 0, 0) then gameOver(p) end return end end if dasKicked then return end local big=p.cheat[3] and 2 or 1 if btnp(2+boffset) or p.ldas>getSpeedValue(p, "DAS") and (p.rdas>p.ldas or p.rdas==0) then tryMove(p, -big, 0) end if btnp(3+boffset) or p.rdas>getSpeedValue(p, "DAS") and (p.rdas=speed[type][x][1] then g=speed[type][x][2] else return g end end return g end function tryMove(p, x, y) local cb=p.currentBlock if checkPosition(p, x, y) then cb.x=cb.x+x cb.y=cb.y+y if y>0 then cb.lifeVal=pieceStartingLD else if cb.resetVal+cb.lifeVal>=cb.resetVal then cb.resetVal=cb.resetVal-(pieceStartingLD-cb.lifeVal) cb.lifeVal=pieceStartingLD else cb.lifeVal=cb.lifeVal+cb.resetVal cb.resetVal=0 end end cb.idle=0 return true end return false end function checkPosition(p, x, y) local cb=p.currentBlock for z=1, #blockoffsets[cb.block][cb.rotation+1] do b=blockoffsets[cb.block][cb.rotation+1][z] if b[2]+y+cb.y<-1 then if b[1]+x+cb.x<1 or b[1]+x+cb.x>10 then return false end else if b[2]+y+cb.y>20 or b[1]+x+cb.x<1 or b[1]+x+cb.x>10 or p.grid[b[2]+y+cb.y][b[1]+x+cb.x]>0 then return false end end end return true end function lock(p) for x=0, 209 do p.gridAge[x//10][x%10+1]=p.gridAge[x//10][x%10+1]+1 end sfx(0, nil, -1, p.id-1) local cb=p.currentBlock local immobile=not (checkPosition(p, -1, 0) or checkPosition(p, 1, 0) or checkPosition(p, 0, -1)) for z=1, #blockoffsets[cb.block][cb.rotation+1] do b=blockoffsets[cb.block][cb.rotation+1][z] if b[2]+cb.y>-1 then p.grid[b[2]+cb.y][b[1]+cb.x]=colormap[p.rule+1][(cb.block-1)%7+1] p.gridAge[b[2]+cb.y][b[1]+cb.x]=-4 end end p.are=getSpeedValue(p, "ARE") checkLines(p) if p.level<1000 then p.blocksUsed=p.blocksUsed+1 else p.rollBlocks=p.rollBlocks-1 end calculateScore(p, immobile) if #p.markedLines==0 and p.amuletActive>0 then amulet(p) end p.currentBlock=nil end function checkLines(p) for y=0, 20 do local count=0 for x=1, 10 do if p.grid[y][x]>0 then count=count+1 end end if count==10 then p.markedLines[#p.markedLines+1]=y end end local lines=math.ceil(#p.markedLines/(p.cheat[3] and 2 or 1)) if #p.markedLines>0 then if p.state=="Roll" then p.rollLines=p.rollLines+lines getGrade(p) end if p.special==2 and not p.slowActive then p.slow=math.min(1200, p.slow+60*lines) end if p.special==3 then p.amulet=math.min(20, p.amulet+lines) end p.are=getSpeedValue(p, "LARE") p.lcf=getSpeedValue(p, "LCF") end end function clearLines(p) if p.lcf~=0 then p.amuletActive=0 end while #p.markedLines>0 do for y=p.markedLines[1], 1, -1 do for x=1, 10 do p.grid[y][x]=p.grid[y-1][x] p.gridAge[y][x]=p.gridAge[y-1][x] end end for x=1, 10 do p.grid[0][x]=0 p.gridAge[0][x]=0 end table.remove(p.markedLines, 1) end if p.currentBlock and not checkPosition(p, 0, 0) then gameOver(p) end end function amulet(p, emergency) if emergency then p.emergencyAmuletTimer=20 end local big=p.cheat[3] and 2 or 1 for x=1, p.amuletActive*big do p.markedLines[x]=20+x-p.amuletActive*big end sfx(19, nil, math.max(p.are, p.emergencyAmuletTimer), p.id-1) end function checkBravo(p) for y=0, 20-#p.markedLines do local count=0 for x=1, 10 do if p.grid[y][x]>0 then count=count+1 end end if count~=0 then return false end end return true end function calculateScore(p, immobile) local lines=math.ceil(#p.markedLines/(p.cheat[3] and 2 or 1)) local lineData={60} local joe = p.special==4 and 1 or 0 if immobile then table.insert(lineData, #lineData, ({"j", "i", "z", "l", "o", "t", "s"})[(p.currentBlock.block-1)%7+1].."-SPIN") end if lines>0 then table.insert(lineData, #lineData, ({"SINGLE", "DOUBLE", "TRIPLE", "JAFTAW"})[lines]) end if checkBravo(p) then table.insert(lineData, #lineData, "bravo") end if immobile and p.level%100<90 then p.cspins=p.cspins+({1, 3, 6, 10, 15})[lines+1] end if lines==0 then p.combo=1 if #lineData>1 then p.lineData=lineData end return elseif lines==4 and p.level%100<90 then p.cquads=p.cquads+1 end local multi=({1, 1.2, 1.5, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 80, 100, 120, 150, 200, 300, 400, 500, 600, 800, 1000, 1200})[math.min(p.cools, (math.min(p.difficulty, 3)*900))//100+1+joe] if immobile and p.special~=1 then p.combo=p.combo+({1, 5, 9, 13})[lines] elseif immobile then p.combo=p.combo+({0, 3, 5, 7})[lines] elseif p.special~=1 then p.combo=p.combo+({1, 4, 7, 10})[lines] else p.combo=p.combo+({0, 2, 3, 4})[lines] end local added=(math.ceil(p.level/4+lines)+p.currentBlock.dropped)*lines*p.combo*(checkBravo(p) and 4 or 1) if p.state~="Versus" then table.insert(lineData, #lineData, "+"..added..(multi>1 and "*"..multi or "")..(p.slowActive and "*0.5" or "")) end p.lineData=lineData p.score=p.score+math.ceil(added*multi/(p.slowActive and 2 or 1)) if p.special==5 then p.blocks=p.blocks+math.ceil(({1, 3, 6, 10})[lines]*(2-p.level/1000)*(immobile and 1.5 or 1)) end local lineTable={ {1, 2, 3, 4}, {1, 2, 3, 5}, {1, 2, 4, 6}, {1, 2, 4, 7}, {1, 3, 5, 7}, } local olevel=p.level if p.state~="Roll" then p.level=p.level+lineTable[p.state=="Versus" and 1 or math.min(5, (p.level//100+p.cools//100)//(2*(p.difficulty+1))+1)][lines] end p.maxBlocks=100-p.cools//100-p.level//20 while p.blocks>p.maxBlocks and p.special==5 do p.blocks=p.blocks-1 p.level=p.level+1 p.maxBlocks=100-p.cools//100-p.level//20 end checkCool(p) p.maxBlocks=100-p.cools//100-p.level//20 while p.blocks>p.maxBlocks and p.special==5 do p.blocks=p.blocks-1 p.level=p.level+1 p.maxBlocks=100-p.cools//100-p.level//20 end if p.state~="Versus" and p.level-p.level%100>olevel then nextSection(p) end if p.state=="Versus" then local attack=lines*math.floor(({1+p.cools//800+joe, 2+p.cools//400+joe, 3+p.cools//200+joe, 4+p.cools//100+joe})[lines]*(immobile and (p.special==0 and ({1, 1.5, 2, 3}) or ({1, 1, 1.5, 2}))[lines] or 1)) player[3-p.id].level=player[3-p.id].level+attack table.insert(lineData, #lineData, "#+"..attack) p.cools=p.cools+({5, 10, 25, 50})[lines] p.cools=p.cools+(immobile and ({0, 0, 15, 50})[lines] or 0) end getGrade(p) end function nextSection(p) p.ctimer=0 p.cquads=0 p.cspins=0 p.cools=p.cools+(p.pendingCools or 0) p.coolsChecked=false p.levelstopped=false local section=(p.level+10)//100+p.pendingCools//100 if p.level>999 then p.level=1000 p.state="RollStart" p.blocks=p.maxBlocks p.menutimer=100 p.track=6 else if p.track>=player[3-p.id].track and not playingMusic then music(p.track%20) playingMusic=true end end end function getGrade(p) local g=0 while grades[g+2] and p.score//100>=grades[g+2]*(p.special==5 and 2 or 1) do g=g+1 end if p.level<1000 then g=math.min(g, 104) elseif p.cools<3000 then g=math.min(g, 106) elseif not p.canIntoTopRank then g=math.min(g, 107+math.min(p.rollLines, 30)//3) end p.grade=g end function rotate(p, dir) local cb=p.currentBlock local big=p.cheat[3] and 2 or 1 local boffset=p.id*8-8 local offsets={{0, 0}, {0, 0}, {0, 0}, {0, 0}} if p.rule==0 then if btnp(2+boffset) or p.ldas>getSpeedValue(p, "DAS") and (p.ldasgetSpeedValue(p, "DAS") and (p.rdas3000 and p.track<5) or (testLevel>2400 and p.track<4) or (testLevel>1800 and p.track<3) or (testLevel>1200 and p.track<2) or (testLevel>600 and p.track<1) or p.difficulty==4 and (p.level>400 and p.track<21) or p.level>900) and p.track>=player[3-p.id].track then p.track=p.track+1 music() playingMusic=false end end --Graphical functions function draw() if menu~=-1 then return drawMenu() end if idle>=1200 then return drawHighScoreList((idle//300-4)%(cfg.joe and 6 or 5)) end if idle>=600 then return drawTitleScreen() end drawPlayer(1) drawPlayer(2) end function printCenter(string, y, color, smallfont) width=print(string, 0, -10, 0, false, 1, smallfont) return print(string, (240-width)/2, y, color, false, 1, smallfont) end function printCenterField(pid, string, y, color, smallfont) width=print(string, 0, -10, 0, false, 1, smallfont) return print(string, 16+120*(pid-1)+(50-width)/2, y, color, false, 1, smallfont) end function drawTitleScreen() printCenter("Master of Blocks 5-X: Devastation", 10, 12) printCenter("PROGRAM & MUSIC - Phoenix Flare", 18, 12) printCenter("GRAPHICS - MarkGamed7794", 26, 12) printCenter("SPECIAL THANKS TO:", 38, 12) printCenter("TAP Discord Server", 46, 12) printCenter("Badger eSports", 54, 12) printCenter("BETA TESTERS:", 66, 12) printCenter("UnderRay - Trixciel - Mizu", 74, 12) printCenter("NOT_A_ROBOT - nothing", 82, 12) if time()%1000>500 then printCenter((cfg.free or credits>=math.max(cfg.coin, 1)) and "PRESS A BUTTON TO START" or "INSERT COIN", 96, 12) end printCenter("Revision 2D", 110, 12) printCenter("", 116, 12) --For use in the event of any major revisions. --Feel free to use this to denote a modified version as well. if cfg.coin<2 then printCenter(cfg.free and "FREE" or (credits.." CREDIT"), 128, 12) else printCenter(cfg.free and "FREE" or ((credits//2)..(credits%2==1 and " 1/2" or "").." CREDIT"), 128, 12) end end function drawHighScoreList(hsdisp) if hsdisp==4 and not cfg.joe then hsdisp=5 end printCenter("HIGH SCORES", 10, 12) printCenter(({[0]="180-ROTATE","HOLD BOX","SLOW MODE","AMULET","Joe","STRATEGY MODE"})[hsdisp], 20, 12) if hsdisp==4 then spr(132, 70, 10, 8, 1, 0, 0, 3, 3) end print("ALL-TIME BEST", 5, 50, 12) print("TODAY'S BEST", 125, 50, 12) for x=1, 3 do font(({"1ST", "2ND", "3RD"})[x], 5, 40+20*x) local hs=highScore.all[hsdisp][x] pal(14, ({[0]=6, 10, 2, 7, 15})[hs[5]]) if hs[5]==4 then pal(12, 14) end drawGrade(hs[0], 20, 40+20*x) pal() if hs[1]<1000 then font("#"..hs[1], 40, 50+20*x) else font("cdefg", 40, 50+20*x) end font(hsdisp==5 and hs[2].."h" or formatTime(hs[2]), 65, 50+20*x) drawName(hs[3], 32, 40+20*x) spr(94+16*hs[4], 65, 40+20*x, 0, 1, 0, 0, 2, 1) end for x=1, 3 do font(({"1ST", "2ND", "3RD"})[x], 125, 40+20*x) local hs=highScore.today[hsdisp][x] pal(14, ({[0]=6, 10, 2, 7, 15})[hs[5]]) if hs[5]==4 then pal(12, 14) end drawGrade(hs[0], 140, 40+20*x) pal() if hs[1]<1000 then font("#"..hs[1], 160, 50+20*x) else font("cdefg", 160, 50+20*x) end font(hsdisp==5 and hs[2].."h" or formatTime(hs[2]), 185, 50+20*x) drawName(hs[3], 152, 40+20*x) spr(94+16*hs[4], 185, 40+20*x, 0, 1, 0, 0, 2, 1) end printCenter(cfg.free and "FREE" or (credits>0 and (credits.." CREDIT") or "INSERT COIN"), 128, 12) end function drawMenu() printCenter("SETTINGS MENU", 10, 12) print("SOUND", 60, 20, menu==0 and 4 or 12) print(cfg.vol.snd, 180-print(cfg.vol.snd, 0, -6), 20, 12) print("MUSIC", 60, 28, menu==1 and 4 or 12) print(cfg.vol.bgm, 180-print(cfg.vol.bgm, 0, -6), 28, 12) print("MENU TIMER", 60, 36, menu==2 and 4 or 12) print(cfg.timer and "ENABLED" or "DISABLED", cfg.timer and 138 or 133, 36, 12) print("PAUSE", 60, 44, menu==3 and 4 or 12) print(cfg.pause and "ENABLED" or "DISABLED", cfg.pause and 138 or 133, 44, 12) print("BGA", 60, 52, menu==4 and 4 or 12) print(cfg.bga and "ENABLED" or "DISABLED", cfg.bga and 138 or 133, 52, 12) print("FREE PLAY", 60, 60, menu==5 and 4 or 12) print(cfg.free and "ENABLED" or "DISABLED", cfg.free and 138 or 133, 60, 12) print("COIN MODE", 60, 68, menu==6 and 4 or 12) print(({"1 CR/COIN", "2 CR/COIN", "2 COIN/CR"})[cfg.coin+1], cfg.coin==0 and 130 or 129, 68, 12) print("RETURN TO GAME", 60, 76, menu==7 and 4 or 12) print("RESET ALL DATA", 60, 84, menu==8 and 4 or 12) if menu<2 then print("PRESS LEFT AND RIGHT TO DECREASE AND INCREASE", 37, 96, 12, false, 1, true) elseif menu<6 then print("PRESS P1 A TO TOGGLE", 84, 96, 12, false, 1, true) elseif menu<7 then print("PRESS LEFT AND RIGHT TO DECREASE AND INCREASE", 37, 96, 12, false, 1, true) elseif menu==7 then print("PRESS P1 A TO EXIT", 88, 96, 12, false, 1, true) elseif menu==8 then print("TO RESET DATA, HOLD P1 A AND B FOR 5 SECONDS", 39, 96, 12, false, 1, true) end print("Joe", 60, 120, menu==9 and 4 or 12) spr(132, 80, 110, 8, 1, 0, 0, 3, 3) print(cfg.joe and "ENABLED" or "", 138, 120, 12) end function drawPlayer(pid) local p=player[pid] local offset=(pid-1)*120 vbank(0) --Draw Field rectb(10+offset, 23, 60, 110, 13) rect(14+offset, 27, 52, 102, 13) rect(15+offset, 28, 50, 100, 0) vbank(1) --Draw Wins if p.wins>0 then print(p.wins.." WINS", 55+offset, 1, 4) elseif p.denyversus and p.special~=5 then print("NO VS", 55+offset, 1, 2) end --Draw Next Boxes vbank(0) print("NEXT", 29+offset, 2, 12) for x=1, 3 do rect(4+x*24+offset, 8, 24, 14, 0) rectb(4+x*24+offset, 8, 24, 14, 13) end --Draw Hold Box if p.special==1 then print("HOLD", 2+offset, 2, 12) rect(2+offset, 8, 24, 14, 0) rectb(2+offset, 8, 24, 14, 13) end --Draw Slow Meter if p.special==2 then local c=p.slowActive and p.slow%2==1 and 4 or (p.slow<300 and 2 or 12) print("SLOW", 2+offset, 2, 12) rect(3+offset, 8, 22, 7, 13) rect(4+offset, 9, 20, 5, 0) rect(4+offset, 9, p.slow/60, 5, c) end --Draw Amulet Meter if p.special==3 then local c=p.amulet<5 and 12 or 10 print("WARD", 2+offset, 2, 12) rect(3+offset, 8, 22, 7, 13) rect(4+offset, 9, 20, 5, 0) rect(4+offset, 9, p.amulet, 5, c) end --Draw Joe if p.special==4 then vbank(1) if p.id==1 then print("Joe", 10+offset, 1, 12) else pal(5, 7) pal(6, 1) end spr(132, 8+offset, 7, 8, 1, 0, 0, 3, 2) pal() vbank(0) end --Draw Block Counter if p.special==5 then font(p.blocks, 20+offset-#(""..p.blocks)*4, 11) pal(14, 12) pal(15, 13) spr(1, 20+offset, 12, 0) pal() end vbank(1) if not p.versus then vbank(0) --Draw Grade if p.difficulty==4 then pal(14, 15) pal(12, 14) end drawGrade(p.grade, 72+offset, 24) pal() vbank(1) --Draw Score font("SCORE", 72+offset, 42) font(p.score, 72+offset, 48) line(72+offset, 56, 70+offset+4*#(p.score..""), 56, 12) if p.grade>=104 and p.level<1000 then font("???", 72+offset, 58) elseif p.grade<107 then local testscore=grades[p.grade+2]*(p.special==5 and 2 or 1) font(testscore.."00", 72+offset, 58) line(72+offset, 56, 70+offset+4*#(testscore.."00"), 56, 12) end end --Draw LineData if p.lineData then for x=1, 7 do pal(blockpalette[x][1], blockpalette[colormap[p.rule+1][x]][1]) end for x=1,#p.lineData-1 do font(p.lineData[x], 72+offset, 62+6*x) end pal() end --Draw Level font("LEVEL", 72+offset, 100) font(p.level, 72+offset, 106) --Draw Timer/Block Counter if p.special~=5 then font("TIME", 72+offset, 118) font(formatTime(p.timer), 72+offset, 124) else font("BLOCKS", 72+offset, 118) font(p.blocksUsed, 72+offset, 124) end --Main per-player draw function if p.state=="Idle" then if player[3-pid].state=="Config" or player[3-pid].state=="Start" or player[3-pid].state=="Win" then printCenterField(pid, "WAIT", 44, 12) else if cfg.free or credits>=math.max(cfg.coin, 1) then printCenterField(pid, "PRESS A", 44, 12) printCenterField(pid, "TO START", 50, 12) else printCenterField(pid, "INSERT", 44, 12) if credits==1 or cfg.coin<2 then printCenterField(pid, "COIN", 50, 12) else printCenterField(pid, "2 COIN", 50, 12) end end end if cheats[pid][1] then printCenterField(pid, "20G MODE", 100, 4) end if cheats[pid][2] then printCenterField(pid, "TLS MODE", 110, 4) end if cheats[pid][3] then printCenterField(pid, "BIG MODE", 120, 4) end elseif p.state=="Config" then if cfg.timer then font((p.menutimer<541 and "0" or "")..(p.menutimer+59)//60, 36+offset, 11) end if p.menu==-1 then printCenterField(pid, "GAMEMODE", 44, 12) printCenterField(pid, p.special==5 and "STRATEGY" or "SPEED", 56, 4+p.special, true) print("Joe", 47+offset, 70, 12) spr(132, 36+offset, 70, 8, 1, 0, 0, 3, 3) if p.special<5 then print("!", 40+offset, 64, 5) spr(162, 16+offset, 70, 0, 1, 0, 0, 2, 2) local a=(time()/100) line(24+offset, 80, 24+offset-math.sin(a)*4, 80+math.cos(a)*4, 2) else print("?", 39+offset, 64, 5) spr(160, 16+offset, 70, 0, 1, 0, 0, 2, 2) end elseif p.menu==0 then printCenterField(pid, "SPECIAL", 44, 12) if p.special==0 then spr(144, 40+offset, 64, 0, 1, 0, 3) spr(145, 32+offset, 64, 0, 1, 0, 1) spr(144, 32+offset, 56, 0, 1, 0, 1) spr(145, 40+offset, 56, 0, 1, 0, 3) spr(146, 36+offset, 60, 0) printCenterField(pid, "180-SPIN", 50, 12) elseif p.special==1 then rectb(28+offset, 57, 24, 14, 13) spr(146, 36+offset, 60, 0) printCenterField(pid, "HOLD", 50, 12) elseif p.special==2 then spr(137, 33+offset, 56, 0, 1, 0, 0, 2, 2) spr(146, 36+offset, 65, 0) printCenterField(pid, "SLOW", 50, 12) elseif p.special==3 then spr(135, 32+offset, 56, 0, 1, 0, 0, 2, 2) spr(146, 25+offset, 60, 0) printCenterField(pid, "AMULET", 50, 12) elseif p.special==4 then if p.id==1 then print("Joe", 40+offset, 50, 12) else pal(5, 7) pal(6, 1) end spr(132, 28+offset, 50, 8, 1, 0, 0, 3, 3) pal() end print(({"Rotate 180\ndegrees by\npressing X\nbutton!", "Hold the\nblock until\nyou need by\npressing X\nbutton!", "Slow the\nblocks for\nlimited time\nwith X press!", "Magic amulet\nfrom before;\npress X to\nuse!", ({"Joe, the\nGreen Alpaca", "Frank, the\nPurple Llama"})[p.id]})[p.special+1], offset+17, 80, 12, false, 1, true) elseif p.menu==1 then printCenterField(pid, "ROTATION", 50, 12) if p.reverse then spr(128, 22+offset, 56, 0, 1, 1, 0, 2, 2) spr(128, 42+offset, 56, 0, 1, 0, 0, 2, 2) printCenterField(pid, "REVERSE", 44, 12) print("Rotation\nbuttons\nare reversed.", offset+17, 80, 12, false, 1, true) else spr(128, 22+offset, 56, 0, 1, 0, 0, 2, 2) spr(128, 42+offset, 56, 0, 1, 1, 0, 2, 2) printCenterField(pid, "STANDARD", 44, 12) print("Rotation\nbuttons\nare standard.", offset+17, 80, 12, false, 1, true) end spr(130, 26+offset, 60, 0) spr(131, 46+offset, 60, 0) elseif p.menu==2 then printCenterField(pid, "DROP TYPE", 44, 12) if p.sonic then printCenterField(pid, "SONIC", 50, 12) spr(9, 24+offset, 65, 0) spr(9, 29+offset, 65, 0, 1, 1) print("Fast drop\ndoesn't lock.\nFor classic\nplayers.", offset+17, 80, 12, false, 1, true) else printCenterField(pid, "HARD", 50, 12) spr(9, 44+offset, 65, 0) spr(9, 49+offset, 65, 0, 1, 1) print("Fast drop\nlocks.\nFor typical\nplayers.", offset+17, 80, 12, false, 1, true) end spr(1, 28+offset, 64, 0) spr(11, 27+offset, 56, 0) spr(1, 48+offset, 64, 0) spr(10, 47+offset, 56, 0) elseif p.menu==3 then printCenterField(pid, "VERSUS", 44, 12) if p.denyversus then printCenterField(pid, "DENY", 50, 12) print("Opponent\nis not able\ninterrupt\nyour game\nto attack.", offset+17, 80, 12, false, 1, true) else printCenterField(pid, "ALLOW", 50, 12) print("Opponent\nis able to\ninterrupt\nyour game\nto attack.", offset+17, 80, 12, false, 1, true) end elseif p.menu==4 then local c={7, 10, 3} if time()%200<100 then c[p.rule+1]=4 end printCenterField(pid, "SELECT", 34, 12) printCenterField(pid, "RULE", 40, 12) printCenterField(pid, "KRS+", 54, c[1], true) printCenterField(pid, "SRS-X", 60, c[2], true) printCenterField(pid, "DRS-H", 66, c[3], true) print(({[0]="Original MoB5\nblock rules.", "Common rule\nused in most\ngames.", "Classic rule\nused by\nexperts.", "Challenge of\nlegendary.\nOnly way to\ngo for top\nrank!"})[p.rule], offset+17, 80, 12, false, 1, true) elseif p.menu==5 then local c={6, 10, 2, 7, 13} if time()%200<100 then c[p.difficulty+1]=4 end printCenterField(pid, "SELECT", 34, 12) printCenterField(pid, "LEVEL", 40, 12) printCenterField(pid, "INNOCENCE", 48, c[1], true) printCenterField(pid, "AWARENESS", 54, c[2], true) printCenterField(pid, "RESOLVE", 60, c[3], true) printCenterField(pid, "DESPERATION", 66, c[4], true) if p.unlock>=13 then printCenterField(pid, "DEVASTATION", 72, c[5], true) end pal() print(({[0]="Easiest mode.\nEveryone\nstarts place.", "Light level.\nFor casual\nplayers.", "Harder level.\nFor used to\ngame players.", "Challenge of\nlegendary.\nOnly way to\ngo for top\nrank!"})[p.difficulty], offset+17, 80, 12, false, 1, true) end if time()%3000<1500 then if p.menu<4 then spr(139, offset+16, 120, 0) spr(140, offset+24, 120, 0) else spr(141, offset+16, 117, 0) spr(157, offset+16, 125, 0) end print("CHANGE", offset+27, 121, 13, false, 1, true) else spr(130, offset+17, 119) print("ACCEPT", offset+27, 121, 13, false, 1, true) end elseif p.state=="Challenge" then if cfg.timer then font((p.menutimer<541 and "0" or "")..(p.menutimer+59)//60, 36+offset, 11) end printCenterField(pid, "ATTACK?", 50, 12) printCenterField(pid, "NO", 60, p.menu==0 and 4 or 12) printCenterField(pid, "YES", 66, p.menu==1 and 4 or 12) elseif p.state=="VersusWarn" then drawGame(pid) rect(p.menutimer*5-480, 62, 1200-p.menutimer*10, 10, 2) if p.menutimer<100 then font("A FIGHT BROKEN OUT!", 77, 65) end elseif p.state=="Start" or p.state=="VersusStart" then if p.menutimer>45 then printCenterField(pid, "READY", 50, 12) else printCenterField(pid, "GO", 50, 12) end drawQueue(pid) elseif p.state=="InGame" or p.state=="Versus" or p.state=="Roll" then drawGame(pid) elseif p.state=="Curtain" then drawGame(pid) rect(15+offset, 128-p.menutimer, 50, p.menutimer, 0) elseif p.state=="NameWait" then printCenterField(pid, "WAIT", 44, 12) elseif p.state=="NameEntry" then if cfg.timer and p.menu~=6 then font((p.menutimer<541 and "0" or "")..(p.menutimer+59)//60, 36+offset, 11) end drawName(p.name, 21+offset, 95) if p.menu>3 and p.menu<6 then spr(257-p.menu, 53+offset, 95, 0) end font("OVERALL", 27+offset, 30) font(({"1ST", "2ND", "3RD", "OUT"})[p.place[1]], 35+offset, 40) font("TODAY", 31+offset, 55) font(({"1ST", "2ND", "3RD"})[p.place[2]], 35+offset, 65) printCenterField(pid, "NAME", 80, 12) printCenterField(pid, "ENTRY", 86, 12) elseif p.state=="GameOver" then printCenterField(pid, "GAME", 74, 2) printCenterField(pid, "OVER", 80, 2) elseif p.state=="Win" then printCenterField(pid, "YOU'RE", 74, 4) printCenterField(pid, "WINNER!", 80, 4) end if p.pause then rect(30+offset, 60, 21, 7, 15) font("PAUSE", 31+offset, 61) end end function drawQueue(pid) local offset=(pid-1)*120 local p=player[pid] for x=1,3 do if p.special==5 and p.difficulty==4 and p.level//100>9-x then break end drawBlock(player[pid].next[x], 0, 11+x*24+offset, 15) end end function drawGame(pid) local p=player[pid] local offset=(pid-1)*120 drawQueue(pid) drawGrid(pid) rectb(14+offset, 27, 52, 102, 13) if #p.markedLines>0 then if p.lcf>0 then for x=1, #p.markedLines do if p.markedLines[x]~=0 and p.lcf%2==0 then rect(15+offset, 23+5*p.markedLines[x], 50, 5, 12) end end elseif p.amuletActive>0 then rect(15+offset, 128-5*#p.markedLines, 50, 5*#p.markedLines, 10) for x=1, p.amuletActive+math.random(p.amuletActive)-1 do local y=128-math.random(5*#p.markedLines) line(15+offset, y, 65+offset, y, 11) end end end if p.currentBlock then if (p.cheat[2] or p.level<100) and gameState~="Curtain" and p.difficulty~=4 then local o=0 while checkPosition(p, 0, o+1) do o=o+1 end drawBlock(42+pid, p.currentBlock.rotation, 10+offset+p.currentBlock.x*5, 23+(p.currentBlock.y+o)*5) end if p.special~=5 or p.level<900 or p.difficulty<4 or p.currentBlock.age<15 then drawBlock(p.currentBlock.block, p.currentBlock.rotation, 10+offset+p.currentBlock.x*5, 23+p.currentBlock.y*5) end end if p.heldBlock then drawBlock(p.heldBlock, 0, 9+offset, 15) end end function drawGrid(pid) local p=player[pid] local offset=(pid-1)*120 local invis=1e50 if p.special==5 and p.difficulty>0 and p.level>499 then invis=55-p.difficulty*5-p.level//25 if p.difficulty==4 then invis=0 end end for y=1,20 do for x=1,10 do if p.grid[y][x]>0 and p.gridAge[y][x]0 and p.gridAge[y][x]<0 then rect(9+offset+5*x, 22+5*y, 7, 7, 12) end end end for y=1,20 do for x=1,10 do if p.grid[y][x]>0 then if p.pause or p.gridAge[y][x]>=invis or invis==0 then rect(10+offset+5*x, 23+5*y, 5, 5, 0) else local b=p.grid[y][x] local bc=blockpalette[b] pal(14, bc[1]) pal(15, bc[2]) spr(1, 10+offset+5*x, 23+5*y, 0) end end end end pal() end function drawBlock(block, rotation, x, y) local blockColor=block>42 and 8 or (block-1)%7+1 local rule=((block-1)//7)%3+1 if block>42 then block=player[block-42].currentBlock.block end for z=1,#blockoffsets[block][rotation+1] do local b=blockoffsets[block][rotation+1][z] local bc=blockpalette[colormap[rule][blockColor]] pal(14, bc[1]) pal(15, bc[2]) spr(1, b[1]*5+x, b[2]*5+y, 0) pal() end end function drawGrade(grade, x, y) grade=math.max(math.min(grade, 118), 0) if grade<8 then spr(16+grade*2, x, y, 0, 1, 0, 0, 2, 2) elseif grade==8 then spr(48, x, y, 0, 1, 0, 0, 2, 2) elseif grade<72 then spr(48+grade//9*2, x, y, 0, 1, 0, 0, 2, 2) spr(112+(grade%9), x+10, y+10, 0) elseif grade<99 then spr(64+grade//9*2, x, y, 0, 1, 0, 0, 2, 2) spr(112+(grade%9), x+10, y+10, 0) elseif grade==99 then spr(86, x, y, 0, 1, 0, 0, 2, 2) elseif grade<105 then spr(86, x, y, 0, 1, 0, 0, 2, 2) spr(21+grade, x+10, y+10, 0) elseif grade<108 then spr(({88, 82, 84})[grade-104], x, y, 0, 1, 0, 0, 2, 2) spr(125, x+10, y+10, 0) elseif grade<117 then spr(90, x, y, 0, 1, 0, 0, 2, 2) spr(grade+4, x+10, y+10, 0) elseif grade==117 then spr(90, x, y, 0, 1, 0, 0, 2, 2) spr(125, x+10, y+10, 0) else spr(92, x, y, 0, 1, 0, 0, 2, 2) end end function drawName(name, x, y) if type(name)=="number" then name={ (name&0xFF0000)>>16, (name&0xFF00)>>8, name&0xFF, } end for z=1, 3 do spr(192+name[z], x+8*z, y, name[z]~=59 and 0 or 8) end end function drawBackground() cls(0) if menu~=-1 or idle>=1200 then cls(8) for x=-2, 6 do for y=-2, 6 do local ex={[0]=-1, 1} spr(86, ex[y%2]*(time()//100)%76+x*76+(y%2)*38, (time()//100)%40+y*20, 0, 1, 0, 0, 2, 2) spr(56, ex[y%2]*(time()//100)%76+x*76+(y%2)*38+16, (time()//100)%40+y*20, 0, 1, 0, 0, 2, 2) spr(24, ex[y%2]*(time()//100)%76+x*76+(y%2)*38+32, (time()//100)%40+y*20, 0, 1, 0, 0, 2, 2) spr(142, ex[y%2]*(time()//100)%76+x*76+(y%2)*38+42, (time()//100)%40+y*20, 0, 1, 0, 0, 2, 2) spr(62, ex[y%2]*(time()//100)%76+x*76+(y%2)*38+52, (time()//100)%40+y*20, 0, 1, 0, 0, 2, 2) spr(124, ex[y%2]*(time()//100)%76+x*76+(y%2)*38+12, (time()//100)%40+y*20+4, 0) end end return end if not cfg.bga then return end local p1=player[1].state=="Idle" and 0 or player[1].level local p2=player[2].state=="Idle" and 0 or player[2].level local level=math.max(p1, p2) if level<100 then for x=0, 6 do circb(120, 68, 20*x+(time()/40)%20, 15) end elseif level<200 then for x=1, 10 do local a={ math.sin(math.rad(x*36+((time()/20)%72<36 and (time()/20) or 0))), math.cos(math.rad(x*36+((time()/20)%72<36 and (time()/20) or 0))), } line(120, 68, 120-a[1]*144, 68+a[2]*144, 12) end elseif level<300 then tri( 120+60*(math.cos(math.rad(90))*math.sin(time()/600)), 68+60*math.sin(math.rad(90)), 120+60*(math.cos(math.rad(210))*math.sin(time()/600)), 68+60*math.sin(math.rad(210)), 120+60*(math.cos(math.rad(330))*math.sin(time()/600)), 68+60*math.sin(math.rad(330)), 14 ) tri( 120+50*(math.cos(math.rad(90))*math.sin(time()/600)), 68+50*math.sin(math.rad(90)), 120+50*(math.cos(math.rad(210))*math.sin(time()/600)), 68+50*math.sin(math.rad(210)), 120+50*(math.cos(math.rad(330))*math.sin(time()/600)), 68+50*math.sin(math.rad(330)), 15 ) elseif level<400 then for x=-1,15 do local h=x*16+(80*(math.cos(math.rad(time()/30))))%16 rect(h, 0, 2, 136, 14) end for y=-1,9 do local h=y*16+(80*(math.sin(math.rad(time()/30))))%16 rect(0, h, 240, 2, 14) end elseif level<500 then for x=1,8 do local y={68+68*math.sin(math.rad(45*x)+time()/700), 68+68*math.sin(math.rad(45*x+80)+time()/700)} line(0, y[1], 240, y[2], 14) end elseif level<600 then h={math.rad(time()/30),math.rad(time()/30+20)} for x=1, 9 do tri( 120, 68, 120+200*math.cos(h[1]+math.rad(40*x)), 68+200*math.sin(h[1]+math.rad(40*x)), 120+200*math.cos(h[2]+math.rad(40*x)), 68+200*math.sin(h[2]+math.rad(40*x)), 15 ) end elseif level<700 then circ(120, 68, 60, 15) for x=1,3 do ellib(120, 68, 60, math.abs(60*math.sin(time()/450+math.rad(60*x))), 12) ellib(120, 68, math.abs(60*math.sin(time()/450+math.rad(90+60*x))), 60, 12) end circb(120, 68, 60, 12) elseif level<800 then local c=(time()/10)%720<360 and 14 or 13 for x=1, 10 do local a={ math.sin(math.rad(x*36+((time()/10)%720<360 and (time()/10) or -(time()/10)))), math.cos(math.rad(x*36+((time()/10)%720<360 and (time()/10) or -(time()/10)))), } line(120, 68, 120-a[1]*144, 68+a[2]*144, c) end circb(120, 68, 720-(time()/10)%360*2, 27-c) circb(120, 68, 1080-(time()/10)%360*3, 27-c) circb(120, 68, 1440-(time()/10)%360*4, 27-c) elseif level<900 then cls(15) for x=0,67 do local v=math.random(5)-3 rect(0, 2*x, 81+60*math.sin(time()/200+x/50)+v, 2, 0) rect(160+v+60*math.sin(time()/200+x/50), 2*x, 140-v, 2, 0) end else cls(1) line(0, 135, 240, 135, 0) end end function drawFieldBorder(p) rect(11+(p.id-1)*120, 24, 58, 108, p.id+1) end --Miscellaneous functions function formatTime(t) local m=t//3600 local s=t//60-m*60 local ms=((t-s*60-m*3600)*100)//60 if m<10 then m="0"..m end if s<10 then s="0"..s end if ms<10 then ms="0"..ms end return m..":"..s..":"..ms end function pal(p1, p2) local addr=0x7FE0 if p1 then p2=p2 or p1 poke4(addr+p1, p2) else for x=0, 15 do poke4(addr+x, x) end end end