summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheav <hheav3@gmail.com>2023-04-04 02:54:40 +0000
committerheav <hheav3@gmail.com>2023-04-04 02:54:40 +0000
commit1afe79eac12e20a6a5783230f6ef30e5befeea52 (patch)
tree34cd5c141d7201563ba285b8c23ba6ada736b47a
parent8dea602c53bbc25a3535d43f2c78a33100f82d35 (diff)
added 40 lines mode, and the concept of modes in general.
-rw-r--r--evloop.lua20
-rw-r--r--game/gfx.lua57
-rw-r--r--game/init.lua39
-rw-r--r--game/modes/40lines.lua30
-rw-r--r--game/music.lua11
-rw-r--r--game/text_events.lua43
-rw-r--r--main.lua2
7 files changed, 180 insertions, 22 deletions
diff --git a/evloop.lua b/evloop.lua
index 637d266..cebf510 100644
--- a/evloop.lua
+++ b/evloop.lua
@@ -7,6 +7,7 @@ local function new(...)
local new = setmetatable({}, M)
new.event_queue = {}
new.tasks = {}
+ new.named_tasks = {}
for i = 1, select("#", ...) do
new:wrap(select(i, ...))
end
@@ -20,7 +21,12 @@ M.new = new
-- create a new task in the event loop.
function M:wrap(fn, ...)
- table.insert(self.event_queue, {coroutine.create(fn), ...})
+ local name = false
+ if type(fn) == "table" then
+ name = fn.name
+ fn = fn[1]
+ end
+ table.insert(self.event_queue, {coroutine.create(fn), name, ...})
end
-- send an event: evloop:queue(event, args...)
@@ -29,6 +35,14 @@ function M:queue(...)
table.insert(self.event_queue, {...})
end
+-- destroy a named task
+function M:kill(name)
+ if self.named_tasks[name] then
+ self.tasks[self.named_tasks[name]] = nil
+ self.named_tasks[name] = nil
+ end
+end
+
local function event_filter(timeout, ...)
local filter = {}
for i = 1, select("#", ...) do
@@ -123,8 +137,10 @@ function M:run()
for _, e in ipairs(q) do
if type(e[1]) == "thread" then
-- start a new task
- if resume_task(e[1], unpack(e, 2)) then
+ local name = e[2]
+ if resume_task(e[1], unpack(e, 3)) then
self.tasks[e[1]] = true
+ if name then self.named_tasks[name] = e[1] end
end
elseif e[1] == "quit" then
return unpack(e, 2)
diff --git a/game/gfx.lua b/game/gfx.lua
index 0f45fc4..c74bc64 100644
--- a/game/gfx.lua
+++ b/game/gfx.lua
@@ -3,6 +3,9 @@ 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
@@ -10,6 +13,7 @@ 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
@@ -25,11 +29,11 @@ local colors = {
function M:field_dimensions()
local padding = 20
- local area_width = 1280 - padding * 2
+ 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 / c, area_height / l
+ local scalex, scaley = (area_width-500) / c, area_height / l
local block_size
if scalex * l < area_height then
block_size = scalex
@@ -89,19 +93,27 @@ function M:draw_tetromino_confined(tetromino, x, y, sidelength, margin)
self:draw_tetromino(tetromino, tetr_x, tetr_y, scale * block_size, true)
end
-function M:draw_hold()
+local function get_hold_size(self)
local block_size, field_x, field_y, field_w, field_h = self:field_dimensions()
- local hold_x = field_x - block_size - block_size/2 - block_size*3/2
- local hold_y = field_y
+ 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", hold_x, hold_y, block_size*1.5 + margin, block_size*1.5 + margin)
+ 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", hold_x, hold_y, block_size*1.5 + margin, block_size*1.5 + margin)
+ 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
- self:draw_tetromino_confined(self.game.hold, hold_x, hold_y, block_size*3/2, margin)
+ 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()
@@ -153,6 +165,34 @@ function M:draw_field()
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:gfind("[^\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)
@@ -164,6 +204,7 @@ function M:draw(dt)
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
diff --git a/game/init.lua b/game/init.lua
index ab55867..e03aa76 100644
--- a/game/init.lua
+++ b/game/init.lua
@@ -39,13 +39,15 @@ function M.new(assets, params)
new.t_spun = false
new.combo = -1
- new.stats = {pieces = 0, lines = 0}
+ new.stats = {pieces = 0, lines = 0, time = 0, start_time = love.timer.getTime()}
new.gravity_delay = 0.5
new.lock_delay = params.lock_delay or 0.8
new.infinity = params.infinity or false
new.das_delay = params.das_delay or 0.16
new.das_repeat_delay = params.das_repeat_delay or 0.03
+
+ new.loop = evloop.new()
return new
end
@@ -171,11 +173,11 @@ function M:place_piece()
local sound = ({"tspinsingle","tspindouble","tspintriple"})[cleared]
self.sfx:play(sound)
end
- self.loop:queue "game.line_clear"
+ self.loop:queue("game.line_clear")
else
self.combo = -1
end
- self.loop:queue "game.piece_placed"
+ self.loop:queue("game.piece_placed")
self:next_piece()
return true
else
@@ -209,15 +211,32 @@ function M:gravity_loop()
end
end
+function M:time_loop()
+ while true do
+ self.loop.poll("update")
+ self.stats.time = love.timer.getTime() - self.stats.start_time
+ end
+end
+
+function M:win()
+ self.loop:kill("input_loop")
+ self.loop:kill("gravity_loop")
+ self.loop:kill("lock_loop")
+ self.loop:kill("das_loop")
+ self.loop:kill("time_loop")
+ self.gfx.text_sidebar[1].text = "you win.\n\n"
+ self.gfx.text_sidebar[1].color = {1, 1, 0}
+ self.music:fade(self.loop, 4)
+end
+
function M:run()
self:next_piece()
- self.loop = evloop.new(
- function() self:input_loop() end,
- function() self:das_loop() end,
- function() self:gravity_loop() end,
- function() self:lock_loop() end,
- function() self.gfx:run() end
- )
+ self.loop:wrap{function() self:input_loop() end, name="input_loop"}
+ self.loop:wrap{function() self:gravity_loop() end, name="gravity_loop"}
+ self.loop:wrap{function() self:lock_loop() end, name="lock_loop"}
+ self.loop:wrap{function() self:das_loop() end, name="das_loop"}
+ self.loop:wrap{function() self:time_loop() end, name="time_loop"}
+ self.loop:wrap{function() self.gfx:run() end, name="gfx"}
return self.loop:run()
end
diff --git a/game/modes/40lines.lua b/game/modes/40lines.lua
new file mode 100644
index 0000000..faf8cb5
--- /dev/null
+++ b/game/modes/40lines.lua
@@ -0,0 +1,30 @@
+local game = require "game"
+local text_events = require "game.text_events"
+
+local M = {}
+M.__index = M
+
+function M.new(assets)
+ local new = {}
+ setmetatable(new, M)
+
+ new.game = game.new(assets, {})
+
+ table.insert(new.game.gfx.text_sidebar, {text="40 lines mode."})
+ table.insert(new.game.gfx.text_sidebar, text_events.pieces(new.game))
+ table.insert(new.game.gfx.text_sidebar, text_events.lines(new.game, 40))
+ table.insert(new.game.gfx.text_sidebar, text_events.time(new.game))
+
+ new.game.loop:wrap(function()
+ while true do
+ new.game.loop.poll("game.line_clear")
+ if new.game.stats.lines >= 40 then
+ new.game:win()
+ end
+ end
+ end)
+
+ return new.game, new
+end
+
+return M \ No newline at end of file
diff --git a/game/music.lua b/game/music.lua
index d9f3e91..d9aa80d 100644
--- a/game/music.lua
+++ b/game/music.lua
@@ -1,6 +1,7 @@
local M = {}
M.__index = M
M.playing = nil
+M.volume = 0.5
function M.new(assets)
local new = setmetatable({}, M)
@@ -15,9 +16,17 @@ function M:play(name)
self.assets.music[name]:seek(0)
M.playing = self.assets.music[name]
end
- self.assets.music[name]:setVolume(0.5)
+ self.assets.music[name]:setVolume(self.volume)
self.assets.music[name]:play()
self.assets.music[name]:setLooping(true)
end
+function M:fade(loop, time)
+ for i=1, math.ceil(time*20) do
+ loop.poll(1/20)
+ local volume = self.volume * (1-i/(time*20))
+ self.playing:setVolume(volume)
+ end
+end
+
return M
diff --git a/game/text_events.lua b/game/text_events.lua
new file mode 100644
index 0000000..ace00eb
--- /dev/null
+++ b/game/text_events.lua
@@ -0,0 +1,43 @@
+local M = {}
+
+M.pieces = function(game)
+ local t = {text="pieces:\n0"}
+ game.loop:wrap(function()
+ while true do
+ local e = game.loop.poll("game.piece_placed")
+ t.text = "pieces:\n"..game.stats.pieces
+ end
+ end)
+ return t
+end
+
+M.lines = function(game, goal)
+ local t = {text="lines:\n0"..(goal and "/"..goal or "")}
+ game.loop:wrap(function()
+ while true do
+ local e = game.loop.poll("game.line_clear")
+ t.text = "lines:\n"..game.stats.lines..(goal and "/"..goal or "")
+ end
+ end)
+ return t
+end
+
+local function display_time(t)
+ local seconds = math.floor(t%60)
+ local minutes = math.floor(t/60)
+ local centiseconds = math.floor((t*100)%100)
+ return ("%02d:%02d:%02d"):format(minutes, seconds, centiseconds)
+
+end
+M.time = function(game)
+ local t = {text="time:\n"..display_time(0)}
+ game.loop:wrap(function()
+ while true do
+ local e = game.loop.poll("update")
+ t.text = "time:\n"..display_time(game.stats.time)
+ end
+ end)
+ return t
+end
+
+return M \ No newline at end of file
diff --git a/main.lua b/main.lua
index 224c3ab..5f47e6a 100644
--- a/main.lua
+++ b/main.lua
@@ -5,7 +5,7 @@ local function main()
evloop.poll "load"
local game_assets = assets.load_from "assets"
evloop.poll "loaded"
- local game_obj = game.new(game_assets, {})
+ local game_obj = require("game.modes.40lines").new(game_assets)
game_obj.music:play("the")
game_obj:run()
evloop:quit()