summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthe lemons <citrons@mondecitronne.com>2023-03-26 03:27:15 -0500
committerthe lemons <citrons@mondecitronne.com>2023-03-26 03:27:15 -0500
commit269a8f2fe621ed281b4115eed963db9815eb1eef (patch)
tree87a6b6949e15af81a9ca9e263e7fa5d5beb50440
parentd427eefea1d6ec979a5af7a511516f1f42b5c6f7 (diff)
game module
-rw-r--r--game/gfx.lua100
-rw-r--r--game/init.lua92
-rw-r--r--game/polyomino.lua43
-rw-r--r--main.lua77
4 files changed, 222 insertions, 90 deletions
diff --git a/game/gfx.lua b/game/gfx.lua
new file mode 100644
index 0000000..e3db203
--- /dev/null
+++ b/game/gfx.lua
@@ -0,0 +1,100 @@
+local evloop = require "evloop"
+local viewport = require "viewport"
+
+local M = {}
+M.__index = M
+
+function M.new(game)
+ local new = setmetatable({game = game}, M)
+ return new
+end
+
+local colors = {
+ ["tetr.Z"] = {1, 0.2, 0.2},
+ ["tetr.I"] = {1, 0.7, 0.2},
+ ["tetr.J"] = {1, 1, 0.2},
+ ["tetr.L"] = {0.2, 1, 0.2},
+ ["tetr.O"] = {0.2, 0.2, 1},
+ ["tetr.S"] = {0.5, 0.2, 0.7},
+ ["tetr.T"] = {0.7, 0.2, 1},
+}
+
+function M:field_dimensions()
+ local padding = 20
+ local area_width = 1280 - padding * 2
+ local area_height = 1080 - padding * 2
+ local c, l = self.game.field.columns, self.game.field.lines
+
+ local scalex, scaley = area_width / 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_block(block, line, column)
+ 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
+ if colors[block] then
+ love.graphics.setColor(unpack(colors[block]))
+ else
+ love.graphics.setColor(1, 1, 1)
+ end
+ love.graphics.rectangle("fill", x, y, block_size, block_size)
+ end
+end
+
+function M:draw_piece()
+ local piece = self.game.piece
+ 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)
+ end
+ end
+ end
+ end
+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(dt)
+ love.graphics.setColor(0.2, 0.2, 0.2)
+ love.graphics.rectangle("fill", 0, 0, 1920, 1080)
+ self:draw_field()
+ self:draw_piece()
+end
+
+function M:loop()
+ local function loop()
+ local _, dt = evloop.poll "draw"
+ self:draw(dt)
+ return loop()
+ end
+ return loop
+end
+
+return M
diff --git a/game/init.lua b/game/init.lua
new file mode 100644
index 0000000..ca071d1
--- /dev/null
+++ b/game/init.lua
@@ -0,0 +1,92 @@
+local evloop = require "evloop"
+local playfield = require "game.playfield"
+local tetrominoes = require "game.tetrominoes"
+local gfx = require "game.gfx"
+
+local M = {}
+M.__index = M
+
+function M.new(params)
+ local new = setmetatable({}, M)
+ new.params = params
+ new.field = playfield.new(params.lines or 20, params.columns or 10)
+ new.gfx = gfx.new(new)
+ new.gravity_delay = 0.5
+ return new
+end
+
+function M:input_loop()
+ local function loop()
+ -- TODO: interface with a remappable input system (it should generate
+ -- its own events)
+ local e, key = evloop.poll("keypressed", "keyreleased")
+ if not self.piece then
+ return loop()
+ end
+
+ if e == "keypressed" then
+ if key == "left" then
+ self.piece:move(0, -1)
+ elseif key == "right" then
+ self.piece:move(0, 1)
+ elseif key == "down" then
+ self.piece:move(-1, 0)
+ elseif key == "up" then
+ self.piece:rotate()
+ elseif key == "space" then
+ repeat until not self.piece:move(-1, 0)
+ self.piece:place()
+ end
+ end
+
+ return loop()
+ end
+ return loop
+end
+
+local pieces = {
+ tetrominoes.i,
+ tetrominoes.j,
+ tetrominoes.l,
+ tetrominoes.o,
+ tetrominoes.s,
+ tetrominoes.t,
+ tetrominoes.z,
+}
+
+function M:next_piece()
+ -- TODO: interface with configurable random system (it should implement
+ -- seeds, bags)
+ self.piece = pieces[love.math.random(#pieces)]:drop(self.field)
+ return self.piece and true or false
+end
+
+function M:gravity_loop()
+ local function loop()
+ evloop.sleep(self.gravity_delay)
+ self.field:remove_cleared()
+ if not self.piece then
+ assert(self:next_piece(), "you lose!")
+ return loop()
+ end
+ if not self.piece:move(-1, 0) then
+ self.piece:place()
+ self.piece = nil
+ end
+ return loop()
+ end
+ return loop
+end
+
+function M:loop()
+ local function loop()
+ evloop.await_any(
+ self:input_loop(),
+ self:gravity_loop(),
+ self.gfx:loop()
+ )
+ end
+ return loop
+end
+
+return M
diff --git a/game/polyomino.lua b/game/polyomino.lua
index 9ae36e9..933a4fd 100644
--- a/game/polyomino.lua
+++ b/game/polyomino.lua
@@ -35,21 +35,6 @@ function M.def(name, shape)
return new
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 + 0.5)
- new.rotation = 1
- if not new:can_occupy() then
- return
- end
- return new
-end
-
local rotations = {
{1, 0, 0, 1},
{0, 1, -1, 0},
@@ -69,10 +54,28 @@ local function rotate(line, column, rotation, size)
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 + 0.5)
+ new.rotation = 1
+ if not new:can_occupy() then
+ return
+ end
+ return new
+end
+
function piece:get_cell(line, column, rotation)
- line, column = rotate(
- line, column, rotation or self.rotation, self.poly.size)
- return self.poly.cells[line + 1][column + 1]
+ return self.poly:get_cell(line, column, rotation or self.rotation)
end
function piece:can_occupy(line, column, rotation)
@@ -90,6 +93,9 @@ function piece: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)
@@ -98,6 +104,7 @@ function piece:place()
end
end
end
+ self.placed = true
end
function piece:rotate(ccw)
diff --git a/main.lua b/main.lua
index 6a53f05..586ccdf 100644
--- a/main.lua
+++ b/main.lua
@@ -1,83 +1,16 @@
local evloop = require "evloop"
-local viewport = require "viewport"
-local playfield = require "game.playfield"
-local tetrominoes = require "game.tetrominoes"
+local game = require "game"
-local field = playfield.new(20, 10)
-local piece
+local game_obj
-local function draw()
- evloop.poll "draw"
-
- local size = viewport.height / field.lines
-
- love.graphics.setColor(0.1, 0.1, 0.1)
- love.graphics.rectangle("fill",
- 0, 0, size * field.columns, size * field.lines)
- love.graphics.setColor(1, 1, 1)
-
- local function draw_block(line, column)
- local x = (column - 1) * size
- local y = viewport.height - line * size
- love.graphics.rectangle("fill", x, y, size, size)
- end
-
- for line = 1, field.lines do
- for column = 1, field.columns do
- if field.cells[line][column] then
- draw_block(line, column)
- end
- end
- end
-
- if piece then
- for line = 0, piece.poly.size - 1 do
- for column = 0, piece.poly.size - 1 do
- if piece:get_cell(line, column) then
- draw_block(piece.line + line, piece.column + column)
- end
- end
- end
- end
-
- return draw()
-end
-
-local function gravity()
- evloop.sleep(0.5)
- field:remove_cleared()
- if not piece then
- piece = tetrominoes.l:drop(field)
- assert(piece, "you lose.")
- else
- if not piece:move(-1, 0) then
- piece:place()
- piece = nil
- end
- end
- return gravity()
-end
-
-local function inputs()
- local _, key = evloop.poll "keypressed"
- if piece then
- if key == "left" then
- piece:move(0, -1)
- elseif key == "right" then
- piece:move(0, 1)
- elseif key == "down" then
- piece:move(-1, 0)
- elseif key == "up" then
- piece:rotate()
- end
- end
- return inputs()
+function love.load()
+ game_obj = game.new {}
end
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
if love.timer then love.timer.step() end
return evloop.mainloop(function()
- evloop.await_any(inputs, gravity, draw)
+ game_obj:loop()()
end)
end