polyflux/game/gfx.lua

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