CC-Tweaked/src/main/resources/data/computercraft/lua/rom/programs/pocket/falling.lua

651 lines
15 KiB
Lua

--[[
Falling - Based on Tetris by Alexey Pajitnov
This version written by Gopher, at the request of Dan200, for
ComputerCraft v1.6. No particular rights are reserved.
--]]
local function colorass(c, bw)
return term.isColor() and c or bw
end
local block_s1 = {
{
{ 1, 0, 0, 0 },
{ 1, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 0, 1, 1, 0 },
{ 1, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "{}"),
fg = colorass(colors.blue, colors.black),
bg = colorass(colors.cyan, colors.white),
}
local block_s2 = {
{
{ 0, 1, 0, 0 },
{ 1, 1, 0, 0 },
{ 1, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 1, 1, 0, 0 },
{ 0, 1, 1, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "{}"),
fg = colorass(colors.green, colors.black),
bg = colorass(colors.lime, colors.white),
}
local block_line = {
{
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 1, 1, 1, 1 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "[]"),
fg = colorass(colors.pink, colors.black),
bg = colorass(colors.red, colors.white),
}
local block_square = {
{
{ 1, 1, 0, 0 },
{ 1, 1, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "[]"),
fg = colorass(colors.lightBlue, colors.black),
bg = colorass(colors.blue, colors.white),
}
local block_L1 = {
{
{ 1, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 1, 1, 1, 0 },
{ 1, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 1, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 1, 0 },
{ 1, 1, 1, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "()"),
fg = colorass(colors.orange, colors.black),
bg = colorass(colors.yellow, colors.white),
}
local block_L2 = {
{
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 1, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 1, 1, 1, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 1, 1, 0 },
{ 0, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 1, 0, 0, 0 },
{ 1, 1, 1, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "()"),
fg = colorass(colors.brown, colors.black),
bg = colorass(colors.orange, colors.white),
}
local block_T = {
{
{ 0, 1, 0, 0 },
{ 1, 1, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 0, 0, 0 },
{ 1, 1, 1, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 1, 0, 0 },
{ 0, 1, 1, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 0 },
},
{
{ 0, 1, 0, 0 },
{ 1, 1, 1, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
},
ch = colorass(" ", "<>"),
fg = colorass(colors.cyan, colors.black),
bg = colorass(colors.purple, colors.white),
}
local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T}
local points = {4, 10, 30, 120}
local function lpad(text, amt)
text = tostring(text)
return string.rep(" ", amt - #text) .. text
end
local width, height = term.getSize()
if height < 19 or width < 26 then
print("Your screen is too small to play :(")
return
end
local speedsByLevel = {
1.2,
1.0,
.8,
.65,
.5,
.4,
.3,
.25,
.2,
.15,
.1,
.05, }
local level = 1
local function playGame()
local score = 0
local lines = 0
local initialLevel = level
local next = blocks[math.random(1, #blocks)]
local pit = {}
local heightAdjust = 0
if height <= 19 then
heightAdjust = 1
end
local function drawScreen()
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
term.setTextColor(colors.black)
term.setBackgroundColor(colorass(colors.lightGray, colors.white))
term.setCursorPos(22, 2)
term.write("Score") --score
term.setCursorPos(22, 5)
term.write("Level") --level
term.setCursorPos(22, 8)
term.write("Lines") --lines
term.setCursorPos(22, 12)
term.write("Next") --next
term.setCursorPos(21, 1)
term.write(" ")
term.setCursorPos(21, 2)
term.write(" ") --score
term.setCursorPos(21, 3)
term.write(" ")
term.setCursorPos(21, 4)
term.write(" ")
term.setCursorPos(21, 5)
term.write(" ") --level
term.setCursorPos(21, 6)
term.write(" ")
term.setCursorPos(21, 7)
term.write(" ")
term.setCursorPos(21, 8)
term.write(" ") --lines
term.setCursorPos(21, 9)
term.write(" ")
term.setCursorPos(21, 10)
term.write(" ")
term.setCursorPos(21, 11)
term.write(" ")
term.setCursorPos(21, 12)
term.write(" ") --next
term.setCursorPos(26, 12)
term.write(" ") --next
term.setCursorPos(21, 13)
term.write(" ")
term.setCursorPos(21, 14)
term.write(" ")
term.setCursorPos(21, 15)
term.write(" ")
term.setCursorPos(21, 16)
term.write(" ")
term.setCursorPos(21, 17)
term.write(" ")
term.setCursorPos(21, 18)
term.write(" ")
term.setCursorPos(21, 19)
term.write(" ")
term.setCursorPos(21, 20)
term.write(" ")
end
local function updateNumbers()
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.setCursorPos(22, 3)
term.write(lpad(score, 5)) --score
term.setCursorPos(22, 6)
term.write(lpad(level, 5)) --level
term.setCursorPos(22, 9)
term.write(lpad(lines, 5)) --lines
end
local function drawBlockAt(block, xp, yp, rot)
term.setTextColor(block.fg)
term.setBackgroundColor(block.bg)
for y = 1, 4 do
for x = 1, 4 do
if block[rot][y][x] == 1 then
term.setCursorPos((xp + x) * 2 - 3, yp + y - 1 - heightAdjust)
term.write(block.ch)
end
end
end
end
local function eraseBlockAt(block, xp, yp, rot)
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
for y = 1, 4 do
for x = 1, 4 do
if block[rot][y][x] == 1 then
term.setCursorPos((xp + x) * 2 - 3, yp + y - 1 - heightAdjust)
term.write(" ")
end
end
end
end
local function testBlockAt(block, xp, yp, rot)
for y = 1, 4 do
local ty = yp + y - 1
for x = 1, 4 do
local tx = xp + x - 1
if block[rot][y][x] == 1 then
if tx > 10 or tx < 1 or ty > 20 or pit[ty][tx] ~= 0 then
return true
end
end
end
end
end
local function pitBlock(block, xp, yp, rot)
for y = 1, 4 do
for x = 1, 4 do
if block[rot][y][x] == 1 then
pit[yp + y - 1][xp + x - 1] = block
end
end
end
end
local function clearPit()
for row = 1, 20 do
pit[row] = {}
for col = 1, 10 do
pit[row][col] = 0
end
end
end
drawScreen()
updateNumbers()
--declare & init the pit
clearPit()
local halt = false
local dropSpeed = speedsByLevel[math.min(level, 12)]
local curBlock = next
next = blocks[math.random(1, 7)]
local curX, curY, curRot = 4, 1, 1
local dropTimer = os.startTimer(dropSpeed)
drawBlockAt(next, 11.5, 15 + heightAdjust, 1)
drawBlockAt(curBlock, curX, curY, curRot)
local function redrawPit()
for r = 1 + heightAdjust, 20 do
term.setCursorPos(1, r - heightAdjust)
for c = 1, 10 do
if pit[r][c] == 0 then
term.setTextColor(colors.black)
term.setBackgroundColor(colors.black)
term.write(" ")
else
term.setTextColor(pit[r][c].fg)
term.setBackgroundColor(pit[r][c].bg)
term.write(pit[r][c].ch)
end
end
end
end
local function hidePit()
for r = 1 + heightAdjust, 20 do
term.setCursorPos(1, r - heightAdjust)
term.setTextColor(colors.black)
term.setBackgroundColor(colors.black)
term.write(" ")
end
end
local function msgBox(message)
local x = math.floor((17 - #message) / 2)
term.setBackgroundColor(colorass(colors.lightGray, colors.white))
term.setTextColor(colors.black)
term.setCursorPos(x, 9)
term.write("+" .. string.rep("-", #message + 2) .. "+")
term.setCursorPos(x, 10)
term.write("|")
term.setCursorPos(x + #message + 3, 10)
term.write("|")
term.setCursorPos(x, 11)
term.write("+" .. string.rep("-", #message + 2) .. "+")
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.setCursorPos(x + 1, 10)
term.write(" " .. message .. " ")
end
local function clearRows()
local rows = {}
for r = 1, 20 do
local count = 0
for c = 1, 10 do
if pit[r][c] ~= 0 then
count = count + 1
else
break
end
end
if count == 10 then
rows[#rows + 1] = r
end
end
if #rows > 0 then
for _ = 1, 4 do
sleep(.1)
for r = 1, #rows do
r = rows[r]
term.setCursorPos(1, r - heightAdjust)
for c = 1, 10 do
term.setTextColor(pit[r][c].bg)
term.setBackgroundColor(pit[r][c].fg)
term.write(pit[r][c].ch)
end
end
sleep(.1)
for r = 1, #rows do
r = rows[r]
term.setCursorPos(1, r - heightAdjust)
for c = 1, 10 do
term.setTextColor(pit[r][c].fg)
term.setBackgroundColor(pit[r][c].bg)
term.write(pit[r][c].ch)
end
end
end
--now remove the rows and drop everythign else
term.setBackgroundColor(colors.black)
for r = 1, #rows do
r = rows[r]
term.setCursorPos(1, r - heightAdjust)
term.write(" ")
end
sleep(.25)
for r = 1, #rows do
table.remove(pit, rows[r])
table.insert(pit, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
end
redrawPit()
lines = lines + #rows
score = score + points[#rows] * math.min(level, 20)
level = math.floor(lines / 10) + initialLevel
dropSpeed = speedsByLevel[math.min(level, 12)]
updateNumbers()
end
sleep(.25)
end
local function blockFall()
if testBlockAt(curBlock, curX, curY + 1, curRot) then
pitBlock(curBlock, curX, curY, curRot)
--detect rows that clear
clearRows()
curBlock = next
curX = 4
curY = 1
curRot = 1
if testBlockAt(curBlock, curX, curY, curRot) then
halt = true
end
drawBlockAt(curBlock, curX, curY, curRot)
eraseBlockAt(next, 11.5, 15 + heightAdjust, 1)
next = blocks[math.random(1, 7)]
drawBlockAt(next, 11.5, 15 + heightAdjust, 1)
return true
else
eraseBlockAt(curBlock, curX, curY, curRot)
curY = curY + 1
drawBlockAt(curBlock, curX, curY, curRot)
return false
end
end
while not halt do
local e = {os.pullEvent()}
if e[1] == "timer" then
if e[2] == dropTimer then
blockFall()
dropTimer = os.startTimer(dropSpeed)
end
elseif e[1] == "key" then
local key = e[2]
local dx, dy, dr = 0, 0, 0
if key == keys.left or key == keys.a then
dx = -1
elseif key == keys.right or key == keys.d then
dx = 1
elseif key == keys.up or key == keys.w then
dr = 1
elseif key == keys.down or key == keys.s then
while not blockFall() do end
dropTimer = os.startTimer(dropSpeed)
elseif key == keys.space then
hidePit()
msgBox("Paused")
while ({os.pullEvent("key")})[2] ~= keys.space do end
redrawPit()
drawBlockAt(curBlock, curX, curY, curRot)
dropTimer = os.startTimer(dropSpeed)
end
if dx + dr ~= 0 then
if not testBlockAt(curBlock, curX + dx, curY + dy, dr > 0 and curRot % #curBlock + dr or curRot) then
eraseBlockAt(curBlock, curX, curY, curRot)
curX = curX + dx
curY = curY + dy
curRot = dr == 0 and curRot or curRot % #curBlock + dr
drawBlockAt(curBlock, curX, curY, curRot)
end
end
elseif e[1] == "term_resize" then
local _, h = term.getSize()
if h == 20 then
heightAdjust = 0
else
heightAdjust = 1
end
redrawPit()
drawBlockAt(curBlock, curX, curY, curRot)
end
end
msgBox("Game Over!")
while true do
local _, k = os.pullEvent("key")
if k == keys.space or k == keys.enter then
break
end
end
level = math.min(level, 9)
end
local selected = 1
local playersDetected = false
local function drawMenu()
term.setBackgroundColor(colors.black)
term.setTextColor(colorass(colors.red, colors.white))
term.clear()
local cx, cy = math.floor(width / 2), math.floor(height / 2)
term.setCursorPos(cx - 6, cy - 2)
term.write("F A L L I N G")
if playersDetected then
if selected == 0 then
term.setTextColor(colorass(colors.blue, colors.black))
term.setBackgroundColor(colorass(colors.gray, colors.white))
else
term.setTextColor(colorass(colors.lightBlue, colors.white))
term.setBackgroundColor(colors.black)
end
term.setCursorPos(cx - 12, cy)
term.write(" Play head-to-head game! ")
end
term.setCursorPos(cx - 10, cy + 1)
if selected == 1 then
term.setTextColor(colorass(colors.blue, colors.black))
term.setBackgroundColor(colorass(colors.lightGray, colors.white))
else
term.setTextColor(colorass(colors.lightBlue, colors.white))
term.setBackgroundColor(colors.black)
end
term.write(" Play from level: <" .. level .. "> ")
term.setCursorPos(cx - 3, cy + 3)
if selected == 2 then
term.setTextColor(colorass(colors.blue, colors.black))
term.setBackgroundColor(colorass(colors.lightGray, colors.white))
else
term.setTextColor(colorass(colors.lightBlue, colors.white))
term.setBackgroundColor(colors.black)
end
term.write(" Quit ")
end
local function runMenu()
drawMenu()
while true do
local event = {os.pullEvent()}
if event[1] == "key" then
local key = event[2]
if key == keys.right or key == keys.d and selected == 1 then
level = math.min(level + 1, 9)
drawMenu()
elseif key == keys.left or key == keys.a and selected == 1 then
level = math.max(level - 1, 1)
drawMenu()
elseif key >= keys.one and key <= keys.nine and selected == 1 then
level = key - keys.one + 1
drawMenu()
elseif key == keys.up or key == keys.w then
selected = selected - 1
if selected == 0 then
selected = 2
end
drawMenu()
elseif key == keys.down or key == keys.s then
selected = selected % 2 + 1
drawMenu()
elseif key == keys.enter or key == keys.space then
break --begin play!
end
end
end
end
while true do
runMenu()
if selected == 2 then
break
end
playGame()
end
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
term.setCursorPos(1, 1)