|
|
|
|
@@ -0,0 +1,384 @@
|
|
|
|
|
local gc=love.graphics
|
|
|
|
|
local rectangle=gc.rectangle
|
|
|
|
|
|
|
|
|
|
local int,abs=math.floor,math.abs
|
|
|
|
|
local rnd,min=math.random,math.min
|
|
|
|
|
local format=string.format
|
|
|
|
|
local ins=table.insert
|
|
|
|
|
local setFont=setFont
|
|
|
|
|
local mStr=mStr
|
|
|
|
|
|
|
|
|
|
local scene={}
|
|
|
|
|
|
|
|
|
|
local board
|
|
|
|
|
local blind
|
|
|
|
|
local startTime,time
|
|
|
|
|
local state,progress
|
|
|
|
|
local skipCD,skipUsed
|
|
|
|
|
local nextTile,nextCD
|
|
|
|
|
local nextPos,prevPos
|
|
|
|
|
local prevSpawnTime=0
|
|
|
|
|
local maxTile
|
|
|
|
|
local score
|
|
|
|
|
|
|
|
|
|
--[[Tiles' value:
|
|
|
|
|
-1: black tile, cannot move
|
|
|
|
|
0: X tile, cannot merge
|
|
|
|
|
1/2/3/...: 2/4/8/... tile
|
|
|
|
|
]]
|
|
|
|
|
local tileColor={
|
|
|
|
|
[-1]=COLOR.black,
|
|
|
|
|
[0]={.5,.3,.3},
|
|
|
|
|
{.93,.89,.85},
|
|
|
|
|
{.93,.88,.78},
|
|
|
|
|
{.95,.69,.47},
|
|
|
|
|
{.96,.58,.39},
|
|
|
|
|
{.96,.49,.37},
|
|
|
|
|
{.96,.37,.23},
|
|
|
|
|
{.93,.81,.45},
|
|
|
|
|
{.93,.80,.38},
|
|
|
|
|
{.93,.78,.31},
|
|
|
|
|
{.93,.77,.25},
|
|
|
|
|
{.93,.76,.18},
|
|
|
|
|
{.40,.37,.33},
|
|
|
|
|
{.22,.19,.17},
|
|
|
|
|
}
|
|
|
|
|
local tileFont={
|
|
|
|
|
80,80,80,--2/4/8
|
|
|
|
|
70,70,70,--16/32/64
|
|
|
|
|
60,60,60,--128/256/512
|
|
|
|
|
55,55,55,55,--1024/2048/4096/8192
|
|
|
|
|
50,50,50,--16384/32768/65536
|
|
|
|
|
45,45,45,--131072/262144/524288
|
|
|
|
|
30,--1048576
|
|
|
|
|
}
|
|
|
|
|
local tileName={[0]="X","2","4","8","16","32","64","128","256","512","1024","2048","4096","8192","16384","32768","65536","131072","262144","524288","2^20"}
|
|
|
|
|
local function airExist()
|
|
|
|
|
for i=1,16 do
|
|
|
|
|
if not board[i]then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local function newTile()
|
|
|
|
|
nextPos=(nextPos+6)%16+1
|
|
|
|
|
local p=nextPos
|
|
|
|
|
while board[p]do
|
|
|
|
|
p=(p-4)%16+1
|
|
|
|
|
end
|
|
|
|
|
board[p]=nextTile
|
|
|
|
|
prevPos=p
|
|
|
|
|
prevSpawnTime=0
|
|
|
|
|
|
|
|
|
|
nextCD=nextCD-1
|
|
|
|
|
if nextCD>0 then
|
|
|
|
|
nextTile=1
|
|
|
|
|
else
|
|
|
|
|
nextTile=rnd()>.1 and 2 or rnd()>.1 and 3 or 4
|
|
|
|
|
nextCD=rnd(8,12)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Fresh score
|
|
|
|
|
score=0
|
|
|
|
|
for i=1,16 do
|
|
|
|
|
if board[i]and board[i]>0 then
|
|
|
|
|
score=score+2^board[i]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
TEXT.show("+"..2^nextTile,1130+rnd(-60,60),555+rnd(-40,40),30,"score",1.5)
|
|
|
|
|
|
|
|
|
|
--Check if board is full
|
|
|
|
|
if airExist()then return end
|
|
|
|
|
|
|
|
|
|
--Check if board is locked in all-directions
|
|
|
|
|
for i=1,12 do
|
|
|
|
|
if board[i]==board[i+4]then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
for i=1,13,4 do
|
|
|
|
|
if
|
|
|
|
|
board[i+0]==board[i+1]or
|
|
|
|
|
board[i+1]==board[i+2]or
|
|
|
|
|
board[i+2]==board[i+3]
|
|
|
|
|
then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Die.
|
|
|
|
|
state=2
|
|
|
|
|
SFX.play(maxTile>=10 and"win"or"fail")
|
|
|
|
|
end
|
|
|
|
|
local function freshMaxTile()
|
|
|
|
|
maxTile=maxTile+1
|
|
|
|
|
if maxTile==7 then skipCD=0 end
|
|
|
|
|
SFX.play("reach")
|
|
|
|
|
ins(progress,format("%s - %.3fs",tileName[maxTile],TIME()-startTime))
|
|
|
|
|
end
|
|
|
|
|
local function squash(L)
|
|
|
|
|
local p1,p2=1
|
|
|
|
|
local moved
|
|
|
|
|
while p1<4 do
|
|
|
|
|
p2=p1+1
|
|
|
|
|
while not L[p2]do
|
|
|
|
|
p2=p2+1
|
|
|
|
|
if p2==5 then
|
|
|
|
|
p1=p1+1
|
|
|
|
|
goto continue
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if not L[p1]then--air←2
|
|
|
|
|
L[p1]=L[p2]
|
|
|
|
|
moved=true
|
|
|
|
|
elseif L[p1]==L[p2]then--2←2
|
|
|
|
|
L[p1]=L[p1]+1
|
|
|
|
|
if L[p1]>maxTile then
|
|
|
|
|
freshMaxTile()
|
|
|
|
|
end
|
|
|
|
|
moved=true
|
|
|
|
|
p1=p1+1
|
|
|
|
|
elseif p1+1~=p2 then--2←4
|
|
|
|
|
L[p1+1]=L[p2]
|
|
|
|
|
moved=true
|
|
|
|
|
p1=p1+1
|
|
|
|
|
else--2,4
|
|
|
|
|
p1=p1+1
|
|
|
|
|
end
|
|
|
|
|
if moved then L[p2]=false end
|
|
|
|
|
::continue::
|
|
|
|
|
end
|
|
|
|
|
return L[1],L[2],L[3],L[4],moved
|
|
|
|
|
end
|
|
|
|
|
local function reset()
|
|
|
|
|
for i=1,16 do board[i]=false end
|
|
|
|
|
progress={}
|
|
|
|
|
state=0
|
|
|
|
|
score=0
|
|
|
|
|
time=0
|
|
|
|
|
maxTile=6
|
|
|
|
|
nextTile,nextPos=1,rnd(16)
|
|
|
|
|
nextCD=32
|
|
|
|
|
skipCD,skipUsed=false,false
|
|
|
|
|
newTile()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function moveUp()
|
|
|
|
|
local moved
|
|
|
|
|
for i=1,4 do
|
|
|
|
|
local m
|
|
|
|
|
board[i],board[i+4],board[i+8],board[i+12],m=squash({board[i],board[i+4],board[i+8],board[i+12]})
|
|
|
|
|
if m then moved=true end
|
|
|
|
|
end
|
|
|
|
|
return moved
|
|
|
|
|
end
|
|
|
|
|
local function moveDown()
|
|
|
|
|
local moved
|
|
|
|
|
for i=1,4 do
|
|
|
|
|
local m
|
|
|
|
|
board[i+12],board[i+8],board[i+4],board[i],m=squash({board[i+12],board[i+8],board[i+4],board[i]})
|
|
|
|
|
if m then moved=true end
|
|
|
|
|
end
|
|
|
|
|
return moved
|
|
|
|
|
end
|
|
|
|
|
local function moveLeft()
|
|
|
|
|
local moved
|
|
|
|
|
for i=1,13,4 do
|
|
|
|
|
local m
|
|
|
|
|
board[i],board[i+1],board[i+2],board[i+3],m=squash({board[i],board[i+1],board[i+2],board[i+3]})
|
|
|
|
|
if m then moved=true end
|
|
|
|
|
end
|
|
|
|
|
return moved
|
|
|
|
|
end
|
|
|
|
|
local function moveRight()
|
|
|
|
|
local moved
|
|
|
|
|
for i=1,13,4 do
|
|
|
|
|
local m
|
|
|
|
|
board[i+3],board[i+2],board[i+1],board[i],m=squash({board[i+3],board[i+2],board[i+1],board[i]})
|
|
|
|
|
if m then moved=true end
|
|
|
|
|
end
|
|
|
|
|
return moved
|
|
|
|
|
end
|
|
|
|
|
local function skip()
|
|
|
|
|
if state==1 and skipCD==0 then
|
|
|
|
|
if airExist()then
|
|
|
|
|
skipCD=32
|
|
|
|
|
skipUsed=true
|
|
|
|
|
newTile()
|
|
|
|
|
SFX.play("hold")
|
|
|
|
|
else
|
|
|
|
|
SFX.play("finesseError")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function scene.sceneInit()
|
|
|
|
|
BG.set("cubes")
|
|
|
|
|
BGM.play("truth")
|
|
|
|
|
board={}
|
|
|
|
|
|
|
|
|
|
blind=false
|
|
|
|
|
startTime,time=0,0
|
|
|
|
|
state=0
|
|
|
|
|
reset()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function scene.mouseDown(x,y,k)
|
|
|
|
|
if k==2 then
|
|
|
|
|
skip()
|
|
|
|
|
else
|
|
|
|
|
local dx,dy=x-640,y-360
|
|
|
|
|
if abs(dx)<320 and abs(dy)<320 and(abs(dx)>60 or abs(dy)>60)then
|
|
|
|
|
scene.keyDown(abs(dx)-abs(dy)>0 and
|
|
|
|
|
(dx>0 and"right"or"left")or
|
|
|
|
|
(dy>0 and"down"or"up")
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function scene.touchDown(_,x,y)
|
|
|
|
|
scene.mouseDown(x,y)
|
|
|
|
|
end
|
|
|
|
|
local moveFunc={
|
|
|
|
|
up=moveUp,
|
|
|
|
|
down=moveDown,
|
|
|
|
|
left=moveLeft,
|
|
|
|
|
right=moveRight,
|
|
|
|
|
}
|
|
|
|
|
function scene.keyDown(key)
|
|
|
|
|
if key=="up"or key=="down"or key=="left"or key=="right"then
|
|
|
|
|
if moveFunc[key]()then
|
|
|
|
|
if state==0 then
|
|
|
|
|
startTime=TIME()
|
|
|
|
|
state=1
|
|
|
|
|
end
|
|
|
|
|
if skipCD and skipCD>0 then
|
|
|
|
|
skipCD=skipCD-1
|
|
|
|
|
if skipCD==0 then
|
|
|
|
|
SFX.play("spin_0")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
newTile()
|
|
|
|
|
SFX.play("move")
|
|
|
|
|
end
|
|
|
|
|
elseif key=="space"then skip()
|
|
|
|
|
elseif key=="r"then reset()
|
|
|
|
|
elseif key=="q"then if state==0 then blind=not blind end
|
|
|
|
|
elseif key=="escape"then SCN.back()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function scene.update(dt)
|
|
|
|
|
if state==1 then
|
|
|
|
|
time=TIME()-startTime
|
|
|
|
|
end
|
|
|
|
|
if prevSpawnTime<1 then
|
|
|
|
|
prevSpawnTime=min(prevSpawnTime+3*dt,1)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function scene.draw()
|
|
|
|
|
setFont(40)
|
|
|
|
|
gc.setColor(1,1,1)
|
|
|
|
|
gc.print(format("%.3f",time),1026,80)
|
|
|
|
|
|
|
|
|
|
--Progress time list
|
|
|
|
|
setFont(30)
|
|
|
|
|
gc.setColor(.7,.7,.7)
|
|
|
|
|
for i=1,#progress do
|
|
|
|
|
gc.print(progress[i],1000,130+32*i)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Score
|
|
|
|
|
setFont(40)
|
|
|
|
|
gc.setColor(1,.7,.7)
|
|
|
|
|
mStr(score,1130,490)
|
|
|
|
|
|
|
|
|
|
--Messages
|
|
|
|
|
if state==2 then
|
|
|
|
|
--Draw no-setting area
|
|
|
|
|
gc.setColor(1,0,0,.3)
|
|
|
|
|
rectangle("fill",15,335,285,250)
|
|
|
|
|
|
|
|
|
|
gc.setColor(.9,.9,0)--win
|
|
|
|
|
elseif state==1 then
|
|
|
|
|
gc.setColor(.9,.9,.9)--game
|
|
|
|
|
elseif state==0 then
|
|
|
|
|
gc.setColor(.2,.8,.2)--ready
|
|
|
|
|
end
|
|
|
|
|
gc.setLineWidth(10)
|
|
|
|
|
rectangle("line",310,30,660,660)
|
|
|
|
|
|
|
|
|
|
--Board
|
|
|
|
|
for i=1,16 do
|
|
|
|
|
if board[i]then
|
|
|
|
|
local x,y=1+(i-1)%4,int((i+3)/4)
|
|
|
|
|
local N=board[i]
|
|
|
|
|
if i~=prevPos or prevSpawnTime==1 then
|
|
|
|
|
gc.setColor(tileColor[N]or COLOR.black)
|
|
|
|
|
rectangle("fill",x*160+163,y*160-117,154,154,15)
|
|
|
|
|
if N>=0 and not blind or i==prevPos then
|
|
|
|
|
gc.setColor(N<3 and COLOR.black or COLOR.W)
|
|
|
|
|
local fontSize=tileFont[N]
|
|
|
|
|
setFont(fontSize)
|
|
|
|
|
mStr(tileName[N],320+(x-.5)*160,40+(y-.5)*160-7*(fontSize/5+1)/2)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
local c=tileColor[N]
|
|
|
|
|
gc.setColor(c[1],c[2],c[3],prevSpawnTime)
|
|
|
|
|
rectangle("fill",x*160+163,y*160-117,154,154,15)
|
|
|
|
|
c=N<3 and 0 or 1
|
|
|
|
|
gc.setColor(c,c,c,prevSpawnTime)
|
|
|
|
|
local fontSize=tileFont[N]
|
|
|
|
|
setFont(fontSize)
|
|
|
|
|
mStr(tileName[N],320+(x-.5)*160,40+(y-.5)*160-7*(fontSize/5+1)/2)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Next indicator
|
|
|
|
|
gc.setColor(1,1,1)
|
|
|
|
|
if nextCD<=12 then
|
|
|
|
|
for i=1,nextCD do
|
|
|
|
|
rectangle("fill",140+i*16-nextCD*8,170,12,12)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Next
|
|
|
|
|
setFont(40)
|
|
|
|
|
mStr("Next",153,185)
|
|
|
|
|
if nextTile>1 then
|
|
|
|
|
gc.setColor(1,.5,.4)
|
|
|
|
|
end
|
|
|
|
|
setFont(70)
|
|
|
|
|
mStr(tileName[nextTile],153,220)
|
|
|
|
|
|
|
|
|
|
--Skip CoolDown
|
|
|
|
|
if skipCD and skipCD>0 then
|
|
|
|
|
setFont(50)
|
|
|
|
|
gc.setColor(1,1,.5)
|
|
|
|
|
mStr(skipCD,160,600)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--Skip mark
|
|
|
|
|
if skipUsed then
|
|
|
|
|
gc.setColor(1,1,.5)
|
|
|
|
|
gc.circle("fill",280,675,10)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--New tile position
|
|
|
|
|
local x,y=1+(prevPos-1)%4,int((prevPos+3)/4)
|
|
|
|
|
gc.setLineWidth(8)
|
|
|
|
|
gc.setColor(.2,.8,0,prevSpawnTime)
|
|
|
|
|
local d=25-prevSpawnTime*25
|
|
|
|
|
rectangle("line",x*160+163-d,y*160-117-d,154+2*d,154+2*d,15)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
scene.widgetList={
|
|
|
|
|
WIDGET.newButton{name="reset", x=160,y=100,w=180,h=100,color="lGreen",font=40,code=pressKey"r"},
|
|
|
|
|
WIDGET.newKey{name="skip", x=160,y=640,w=180,h=100,color="lYellow",font=40,code=pressKey"space",hide=function()return state~=1 or not skipCD or skipCD>0 end},
|
|
|
|
|
WIDGET.newSwitch{name="blind", x=240,y=370,w=60, font=40,disp=function()return blind end, code=pressKey"q",hide=function()return state==1 end},
|
|
|
|
|
WIDGET.newButton{name="back", x=1140,y=640,w=170,h=80,font=40,code=backScene},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return scene
|