minetest-laptop/apps/tetris_app.lua

277 lines
6.8 KiB
Lua

-- Based on https://github.com/minetest-mods/homedecor_modpack/blob/master/computer/tetris.lua
local shapes = {
{ { x = {0, 1, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 1, 1, 1}, y = {0, 1, 2, 3} },
{ x = {0, 1, 2, 3}, y = {1, 1, 1, 1} } },
{ { x = {0, 0, 1, 1}, y = {0, 1, 1, 2} },
{ x = {1, 2, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 0, 1, 0}, y = {0, 1, 1, 2} },
{ x = {0, 1, 1, 2}, y = {0, 0, 1, 1} } },
{ { x = {1, 2, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 2} },
{ x = {1, 1, 0, 1}, y = {0, 1, 2, 2} },
{ x = {0, 0, 1, 2}, y = {0, 1, 1, 1} } },
{ { x = {1, 1, 1, 2}, y = {0, 1, 2, 2} },
{ x = {0, 1, 2, 0}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 0} } },
{ { x = {1, 0, 1, 2}, y = {0, 1, 1, 1} },
{ x = {1, 1, 1, 2}, y = {0, 1, 2, 1} },
{ x = {0, 1, 2, 1}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {1, 0, 1, 2} } } }
local base_color_texture = "default_diamond_block.png"
local base_color_alpha = '128'
colors = { base_color_texture.."^[colorize:#00FFFF:"..base_color_alpha, base_color_texture.."^[colorize:#FF00FF:"..base_color_alpha, base_color_texture.."^[colorize:#FF0000:"..base_color_alpha,
base_color_texture.."^[colorize:#0000FF:"..base_color_alpha, base_color_texture.."^[colorize:#00FF00:"..base_color_alpha, base_color_texture.."^[colorize:#FF4500:"..base_color_alpha,
base_color_texture.."^[colorize:#FFFF00:"..base_color_alpha }
local boardx, boardy = 0, 0
local sizex, sizey, size = 0.42, 0.44, 0.47
local comma = ","
local semi = ";"
local close = "]"
local concat = table.concat
local insert = table.insert
local draw_shape = function(id, x, y, rot, posx, posy)
local d = shapes[id][rot]
local scr = {}
local ins = #scr
for i=1,4 do
local tmp = { "image[",
(d.x[i]+x)*sizex+posx, comma,
(d.y[i]+y)*sizey+posy, semi,
size, comma, size, semi,
colors[id], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
return concat(scr)
end
local tetris_class = {}
tetris_class.__index = tetris_class
function get_tetris(app, data)
local self = setmetatable({}, tetris_class)
self.data = data
self.app = app
return self
end
function tetris_class:new_game()
local nex = math.random(7)
self.data.t = {
board = {},
boardstring = "",
previewstring = draw_shape(nex, 0, 0, 1, 6.5, 0.8),
score = 0,
cur = math.random(7),
nex = nex,
x=4, y=0, rot=1
}
self.app:get_timer():start(0.3)
end
function tetris_class:update_boardstring()
local scr = {}
local ins = #scr
for i, line in pairs(self.data.t.board) do
for _, tile in pairs(line) do
local tmp = { "image[",
tile[1]*sizex+boardx, comma,
i*sizey+boardy, semi,
size, comma, size, semi,
colors[tile[2]], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
end
self.data.t.boardstring = concat(scr)
end
function tetris_class:add()
local t = self.data.t
local d = shapes[t.cur][t.rot]
for i=1,4 do
local l = d.y[i] + t.y
if not t.board[l] then t.board[l] = {} end
insert(t.board[l], {d.x[i] + t.x, t.cur})
end
end
function tetris_class:scroll(l)
for i=l, 1, -1 do
self.data.t.board[i] = self.data.t.board[i-1] or {}
end
end
function tetris_class:check_lines()
for i, line in pairs(self.data.t.board) do
if #line >= 10 then
self:scroll(i)
self.data.t.score = self.data.t.score + 20
end
end
end
function tetris_class:check_position(x, y, rot)
local d = shapes[self.data.t.cur][rot]
for i=1,4 do
local cx, cy = d.x[i]+x, d.y[i]+y
if cx < 0 or cx > 9 or cy < 0 or cy > 19 then
return false
end
for _, tile in pairs(self.data.t.board[ cy ] or {}) do
if tile[1] == cx then return false end
end
end
return true
end
function tetris_class:stuck()
local t = self.data.t
if self:check_position(t.x, t.y+1, t.rot) then
return false
else
return true
end
end
function tetris_class:tick()
local t = self.data.t
if self:stuck() then
if t.y <= 0 then
return false
end
self:add()
self:check_lines()
self:update_boardstring()
t.cur, t.nex = t.nex, math.random(7)
t.x, t.y, t.rot = 4, 0, 1
t.previewstring = draw_shape(t.nex, 0, 0, 1, 6.5, 0.8)
else
t.y = t.y + 1
end
return true
end
function tetris_class:move(dx, dy)
local t = self.data.t
local newx, newy = t.x+dx, t.y+dy
if not self:check_position(newx, newy, t.rot) then
return
end
t.x, t.y = newx, newy
end
function tetris_class:rotate(dr)
local t = self.data.t
local no = #(shapes[t.cur])
local newrot = (t.rot+dr) % no
if newrot<1 then newrot = newrot+no end
if not self:check_position(t.x, t.y, newrot) then
return
end
t.rot = newrot
end
function tetris_class:key(fields)
local t = self.data.t
if fields.left then
self:move(-1, 0)
end
if fields.rotateleft then
self:rotate(-1)
end
if fields.down then
t.score = t.score + 1
self:move(0, 1)
end
if fields.drop then
while not self:stuck() do
t.score = t.score + 2
self:move(0, 1)
end
end
if fields.rotateright then
self:rotate(1)
end
if fields.right then
self:move(1, 0)
end
end
laptop.register_app("tetris", {
app_name = "Tetris",
app_icon = "laptop_tetris_icon.png",
app_info = "Tetris",
formspec_func = function(app, mtos)
local data = mtos.bdev:get_app_storage('ram', 'tetris')
local tetris = get_tetris(app, data)
local timer = minetest.get_node_timer(mtos.pos)
if not data.t then
return mtos.theme:get_button('2,4;2,2', 'major', 'new', 'New Game', 'Start a new game')
end
local buttons = mtos.theme:get_button('6,6;1,1', 'minor', 'left', '<')..
mtos.theme:get_button('6,5;1,1', 'minor', 'rotateleft', 'L')..
mtos.theme:get_button('7,5;1,1', 'minor', 'down', 'v')..
mtos.theme:get_button('7,6;1,1', 'minor', 'drop', 'V')..
mtos.theme:get_button('8,5;1,1', 'minor', 'rotateright', 'R')..
mtos.theme:get_button('8,6;1,1', 'minor', 'right', '>')..
mtos.theme:get_button('6,3.5;3,1', 'major', 'new', 'New Game', 'Start a new game')
local t = tetris.data.t
return 'container[3,1]background[0,-0.05;4.35,9;'.. mtos.theme.contrast_background .. ']' ..
t.boardstring .. t.previewstring ..
draw_shape(t.cur, t.x, t.y, t.rot, boardx, boardy) ..
mtos.theme:get_label('6.5,0.1', 'Next...') ..
mtos.theme:get_label('6.5,2.7', 'Score:...'..t.score) ..
buttons .. 'container_end[]'
end,
receive_fields_func = function(app, mtos, sender, fields)
local data = mtos.bdev:get_app_storage('ram', 'tetris')
local tetris = get_tetris(app, data)
if fields.new then
tetris:new_game()
elseif fields.continue then
app:get_timer():start(0.3)
else
tetris:key(fields)
end
end,
on_timer = function(app, mtos)
local data = mtos.bdev:get_app_storage('ram', 'tetris')
if not data.t then
return false
else
return get_tetris(app, data):tick()
end
end,
})