local M = {}
M.__index = M
local evloop = require "evloop"
local debug_gfx = require "game.debug_gfx"
function M.def(name, shape, kick_table)
local new = setmetatable({name = name}, M)
new.kick_table = kick_table or {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}
new.cells = {}
new.last_rotated = false
new.t_spun = false
new.last_kick_id = 0
for l in shape:gmatch "[^%s]+" do
local line = {}
new.size = #l
for i = 1, #l do
local c = l:sub(i, i)
if c ~= "." then
if c == "#" then
line[i] = new.name
else
line[i] = new.name .. "." .. c
end
end
end
table.insert(new.cells, 1, line)
end
new.bottom = new.size
new.top = 1
new.left_side = new.size
new.right_side = 1
for line = 1, new.size do
for column = 1, new.size do
if new.cells[line][column] then
if new.bottom > line then new.bottom = line end
if new.right_side < column then new.right_side = column end
if new.top < line then new.top = line end
if new.left_side > column then new.left_side = column end
end
end
end
new.height = new.top - new.bottom + 1
new.width = new.right_side - new.left_side + 1
return new
end
local rotations = {
{1, 0, 0, 1},
{0, 1, -1, 0},
{-1, 0, 0, -1},
{0, -1, 1, 0},
}
local function rotate(line, column, rotation, size)
local center = size / 2 - 0.5
line = line - center
column = column - center
local rotation = rotations[rotation or rotation]
local l = line * rotation[1] + column * rotation[2]
local c = line * rotation[3] + column * rotation[4]
line = l + center
column = c + center
return line, column
end
function M:get_cell(line, column, rotation)
line, column = rotate(line, column, rotation or 1, self.size)
return self.cells[line + 1][column + 1]
end
local piece = {}
piece.__index = piece
function M:drop(field)
local new = setmetatable({poly = self}, piece)
new.field = field
new.line = field.lines - (self.bottom - 1)
new.column = math.floor(field.columns / 2 - self.size / 2 + 1)
new.rotation = 1
if not new:can_occupy() then
return
end
return new
end
function piece:get_cell(line, column, rotation)
return self.poly:get_cell(line, column, rotation or self.rotation)
end
function piece:can_occupy(line, column, rotation)
line = line or self.line
column = column or self.column
for l = 0, self.poly.size - 1 do
for c = 0, self.poly.size - 1 do
if self:get_cell(l, c, rotation)
and self.field:cell_full(line + l, column + c) then
return false
end
end
end
return true
end
function piece:can_move(lines, columns, rotation)
local rotation = self.rotation + (rotation or 0)
if rotation > 4 then
rotation = 1
elseif rotation < 1 then
rotation = 4
end
local line, column = self.line + lines or 0, self.column + columns or 0
return self:can_occupy(line, column, rotation)
end
function piece:place()
if self.placed then
return
end
for line = 0, self.poly.size - 1 do
for column = 0, self.poly.size - 1 do
local cell = self:get_cell(line, column)
if cell then
self.field.cells[self.line + line][self.column + column] = cell
end
end
end
self.placed = true
end
function piece:t_spin_check()
if self.poly.name ~= "tetr.T" then return false end
if self.last_kick_id == 5 then return true end
local check_points = {{0,0},{2,2},{0,2},{2,0}}
local corners_total = 0
for i, v in ipairs(check_points) do
if self.field.cells[self.line+v[1]][self.column+v[2]] then corners_total = corners_total + 1 end
end
return corners_total >= 3
end
function piece:rotate(ccw)
local rotation = self.rotation + (ccw and -1 or 1)
if rotation > 4 then
rotation = 1
elseif rotation < 1 then
rotation = 4
end
for i,v in ipairs(self.poly.kick_table[ccw and rotation or self.rotation]) do
local mul = ccw and -1 or 1
local ty = self.line + v[2] * mul
local tx = self.column + v[1] * mul
--[[ debug rotation
if i ~= 1 then
evloop.paused = true
local shadow = self.poly:drop(self.field)
shadow.line = ty
shadow.column = tx
shadow.rotation = rotation
debug_gfx.push(function(self)
self:draw_piece_general(shadow, "only visual")
end)
evloop.debug_sleep(0.6)
debug_gfx.pop()
evloop.paused = false
end
]]
if self:can_occupy(ty, tx, rotation) then
self.rotation = rotation
self.line = ty
self.column = tx
self.last_rotated = true
self.last_kick_id = i
self.t_spun = self:t_spin_check()
return true
end
end
return false
end
function piece:move(lines, columns)
if self:can_move(lines, columns) then
self.line = self.line + lines or 0
self.column = self.column + columns or 0
self.last_rotated = false
self.t_spun = false
return true
end
return false
end
return M