local evloop = require "evloop"
local viewport = require "viewport"
local tetrominoes = require "game/tetrominoes"
local debug_gfx = require "game.debug_gfx"
local font = love.graphics.newFont(20)
love.graphics.setFont(font)
local M = {}
M.__index = M
function M.new(assets, game)
local new = setmetatable({}, M)
new.assets = assets
new.game = game
new.text_sidebar = {{text = "this won't save you from the mind spiders.\n\n"}}
return new
end
local colors = {
["tetr.Z"] = {0, 1, 1},
["tetr.I"] = {0.5, 1, 1},
["tetr.J"] = {0.67, 1, 1},
["tetr.L"] = {0.08, 1, 1},
["tetr.O"] = {0.16, 1, 1},
["tetr.S"] = {0.33, 1, 1},
["tetr.T"] = {0.83, 1, 1},
}
function M:field_dimensions()
local padding = 20
local area_width = 1920 - padding * 2
local area_height = 1080 - padding * 2
local c, l = self.game.field.columns, self.game.field.lines
local scalex, scaley = (area_width-500) / c, area_height / l
local block_size
if scalex * l < area_height then
block_size = scalex
else
block_size = scaley
end
local x = padding + area_width / 2 - c * block_size / 2
local y = padding + area_height / 2 - l * block_size / 2
local w, h = block_size * c, block_size * l
return block_size, x, y, w, h
end
function M:draw_square(block, x, y, block_size, shadow)
love.graphics.setColor(1, 1, 1, shadow and 0.2 or 1)
local hueshift = self.assets.shader.hueshift
love.graphics.setShader(hueshift)
local hsv = colors[block] or {0, 0, 1}
--hueshift:send("colorize_to",{(hsv[1] + love.timer.getTime()/5) % 1, math.sin(math.asin(hsv[2])+hsv[1]*math.pi*2+love.timer.getTime()/5), hsv[3]})
hueshift:send("colorize_to", hsv)
local img = self.assets.img.block
local img_w, img_h = img:getDimensions()
love.graphics.draw(img, x, y, 0, block_size / img_w, block_size / img_h)
end
function M:draw_block(block, line, column, shadow)
local block_size, field_x, field_y, _, field_h = self:field_dimensions()
local x = field_x + (column - 1) * block_size
local y = field_y + field_h - line * block_size
if block then self:draw_square(block, x, y, block_size, shadow) end
end
function M:draw_tetromino(tetromino, x, y, block_size, trim_margins)
for yy, line in pairs(tetromino.cells) do
for xx, cell in pairs(line) do
local xxx = xx
local yyy = yy
yyy = tetromino.size - yyy + 1
if trim_margins then
xxx = xxx - tetromino.left_side + 1
yyy = yyy - (tetromino.size-tetromino.top)
end
self:draw_square(cell, x + (xxx-1)*block_size, y + (yyy-1)*block_size, block_size)
end
end
end
function M:draw_tetromino_confined(tetromino, x, y, sidelength, margin)
local dim = math.max(tetromino.width, tetromino.height)
local block_size = sidelength/3
local scale = 3/math.max(dim, 3)
local tetr_x = x + margin/2 + (3-tetromino.width*scale)/2 * block_size
local tetr_y = y + margin/2 + (3-tetromino.height*scale)/2 * block_size
self:draw_tetromino(tetromino, tetr_x, tetr_y, scale * block_size, true)
end
local function get_hold_size(self)
local block_size, field_x, field_y, field_w, field_h = self:field_dimensions()
local x = field_x - block_size - block_size/2 - block_size*3/2
local y = field_y
local margin = block_size/2
local inner_size = block_size*3/2
local size = inner_size + margin
return x, y, margin, inner_size, size
end
function M:draw_hold()
local block_size, field_x, field_y, field_w, field_h = self:field_dimensions()
local x, y, margin, size = get_hold_size(self)
love.graphics.setColor(0, 0, 0)
love.graphics.rectangle("fill", x, y, block_size*1.5 + margin, block_size*1.5 + margin)
if not self.game.can_hold then
love.graphics.setColor(0.5, 0.5, 0.5, 0.5)
love.graphics.rectangle("fill", x, y, block_size*1.5 + margin, block_size*1.5 + margin)
end
if not self.game.hold then return end
hold_size = block_size*3/2 + margin -- to be fair this is better described as padding.
self:draw_tetromino_confined(self.game.hold, x, y, size, margin)
end
function M:draw_queue()
local block_size, field_x, field_y, field_w, field_h = self:field_dimensions()
local queue = self.game.bag:lookahead(5)
local margin = block_size/2
local x, y = field_x + field_w + block_size, field_y
love.graphics.setColor(0, 0, 0)
love.graphics.rectangle("fill", x, y, block_size*1.5 + margin, (block_size*1.5 + margin) * 5)
for i=1, #queue do
self:draw_tetromino_confined(queue[i], x, y + (i-1)*(block_size*1.5 + margin), block_size*1.5, margin)
end
end
function M:draw_piece_general(piece,shadow)
if shadow and shadow ~= "only visual" then
local old_piece = piece
piece = piece.poly:drop(self.game.field)
piece.line = old_piece.line
piece.column = old_piece.column
piece.rotation = old_piece.rotation
while piece:move(-1,0) do end
end
if not piece then return end
for l = 0, piece.poly.size - 1 do
if piece.line + l <= self.game.field.lines then
for c = 0, piece.poly.size - 1 do
local block = piece:get_cell(l, c)
if block then
self:draw_block(block, piece.line + l, piece.column + c, shadow)
end
end
end
end
end
function M:draw_piece(shadow)
self:draw_piece_general(self.game.piece, shadow)
end
function M:draw_field()
local field = self.game.field
local _, x, y, w, h = self:field_dimensions()
love.graphics.setColor(0.1, 0.1, 0.1)
love.graphics.rectangle("fill", x, y, w, h)
for line = 1, field.lines do
for column = 1, field.columns do
self:draw_block(field.cells[line][column], line, column)
end
end
end
function M:draw_game_text()
local font = love.graphics.getFont()
local line_height = font:getHeight() * 3/2
local hold_x, hold_y, _, _, hold_size = get_hold_size(self)
local text_end_x = hold_x + hold_size
local text_y = hold_y + hold_size + line_height
for i, obj in ipairs(self.text_sidebar) do
local y = text_y + (i-1) * line_height
local newlines = 0
for j=1, #obj.text do
if obj.text:sub(j,j) == "\n" then newlines = newlines + 1 end
end
text_y = text_y + font:getHeight() * newlines
if obj.color then
love.graphics.setColor(unpack(obj.color))
else
love.graphics.setColor(1, 1, 1)
end
local j = 0
for text in obj.text:gmatch("[^\n]+") do
local w = font:getWidth(text)
local x = text_end_x - w
love.graphics.print(text, x, y + font:getHeight() * j)
j = j + 1
end
end
end
function M:draw(dt)
love.graphics.setColor(0.2, 0.2, 0.2)
love.graphics.rectangle("fill", 0, 0, 1920, 1080)
self:draw_field()
self:draw_piece()
self:draw_hold()
self:draw_queue()
if self.game.piece then
self:draw_piece(true) -- shadow.
end
self:draw_game_text()
for i=1, #debug_gfx.stack do
debug_gfx.stack[i](self)
end
end
function M:run()
for _, dt in evloop.events "draw" do
self:draw(dt)
end
end
return M