diff options
author | the lemons <citrons@mondecitronne.com> | 2023-03-26 03:27:15 -0500 |
---|---|---|
committer | the lemons <citrons@mondecitronne.com> | 2023-03-26 03:27:15 -0500 |
commit | 269a8f2fe621ed281b4115eed963db9815eb1eef (patch) | |
tree | 87a6b6949e15af81a9ca9e263e7fa5d5beb50440 | |
parent | d427eefea1d6ec979a5af7a511516f1f42b5c6f7 (diff) |
game module
-rw-r--r-- | game/gfx.lua | 100 | ||||
-rw-r--r-- | game/init.lua | 92 | ||||
-rw-r--r-- | game/polyomino.lua | 43 | ||||
-rw-r--r-- | main.lua | 77 |
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) @@ -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 |