diff --git a/tetris/modes/a3minus.lua b/tetris/modes/a3minus.lua new file mode 100644 index 0000000..2bdc2f2 --- /dev/null +++ b/tetris/modes/a3minus.lua @@ -0,0 +1,15 @@ +local GameMode = require 'tetris.modes.gamemode' +local SurvivalA3Game = require 'tetris.modes.survival_a3' + +local A3Minus = SurvivalA3Game:extend() + +A3Minus.name = "Survival A3-" +A3Minus.hash = "A3Minus" +A3Minus.tagline = "A training version of Survival A3." + +function A3Minus:initialize(ruleset) + self.torikan_time = math.huge + GameMode.initialize(self, ruleset) +end + +return A3Minus \ No newline at end of file diff --git a/tetris/modes/fpviewer.lua b/tetris/modes/fpviewer.lua new file mode 100644 index 0000000..a669776 --- /dev/null +++ b/tetris/modes/fpviewer.lua @@ -0,0 +1,2081 @@ +local GameMode = require 'tetris.modes.gamemode' + +local FlashPointViewer = GameMode:extend() + +FlashPointViewer.name = "FP Board Viewer" +FlashPointViewer.hash = "FPViewer" +FlashPointViewer.tagline = "See all of Flash Point's boards!" + +local board_str = [[ + + + + + + + + + CC + ZZ + ZZ + SSSS + SSSS + SSSS + TTTTTT + TTTTTT + TTTTTT + OOOOOOOO + OOOOOOOO + OOOOOOOO + + + + + + + + + + + +Z Z Z Z +Z Z Z Z +Z Z Z Z +Z Z C Z +Z Z Z Z +Z Z Z Z +Z Z Z Z +Z Z Z Z +Z Z Z Z + + + + + + + + + + + + LLLLL + C L + L + L + LLLLL + L + L + L + LLLLL + + + + + + + + + + + +C Z + ZZ + ZZZ + ZZZZ + ZZZZZ + ZZZZZZ + ZZZZZZZ + ZZZZZZZZ + ZZZZZZZZZ + + + + + + + SS + SS + CC + SS + SS + SS + + + LL + LL + CC + LL + LL + LL + + + + + + + + + + + +SSSSSSSS + +SSSS SS + + SS SS + +CSSS SS + +SSSSSSSS + + + + + + + + + + + + C +L L +L L +LL LL +LL LL +LLL LLL +LLL LLL +LLLL LLLL +LLLL LLLL + + + + + + + + + + + + + + + JJJJ + J J + JC J + JJJJ + + + + + + + + + + + + + + + SSSSSSS + + +SSSSSSS + + C + + + + + + + + + + OO + OO + OOOO + OOOO + OOOOOO + OOOOOO + OOOO + OOOO + OO + CO + + + + + + + + + + + + + + + TTTT + T + T + T + TTTT + T C + T + T + TTTT + + + + + + + + + + + +ZZ ZZ + + +ZZZZ ZZZZ + + +ZZ C ZZ + + + + + + + + + + + + + + + + + +T T + T T + T T + T C + TT + + + + I L + I L + I L + I L + I L + I L + I L + I L + C L + I L + I L + I L + I L + I L + I L + I L + I L + + + + + + + + + IIII + II + II +I II I + II + II + IIII + CII + II +I II I + II + II + + + + + + + + + + + + + + Z Z + Z Z Z + C Z + Z + Z + ZZZZZ + Z + + + + + OOOO + OOOO + OOOO + OOOO + OOOO + OOOO + + C + OOOO + OOOO + OOOO + OOOO + OOOO + OOOO + + + + + + + + + + + + + + + SS + SS SS +SS SS + SS SS + SS + + + C + + + +C +Z Z +Z Z +Z Z +Z Z +ZZ ZZ +ZZ ZZ +ZZ ZZ +ZZ ZZ +ZZZ ZZZ +ZZZ ZZZ +ZZZ ZZZ +ZZZ ZZZ +ZZZZ ZZZZ +ZZZZ ZZZZ +ZZZZ ZZZZ +ZZZZ ZZZZ + + + + + + + + + + + + + ZZ Z + Z Z Z + Z Z Z + Z ZZ + Z Z + + C + + + + + + ZZZZ + Z + Z + Z +ZZZZ + + + + + ZZZZ + Z + Z + ZZZZ + C + + + + + + + + + + + + L + LLL + LLLLL + + + L + LLL + LLLLL + C + + + + + +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z C T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O +Z S T O + + + + + IIIIII + + + +IIIIII + + + + IIIIII + + + +IIICII + + + + + + + + + + + + + + + + + + + IIIIIIII +I IIIIII I +II IIII II +III II III +IIII IIIC + +Z Z + + +ZZ ZZ + + +ZZZ ZZZ + + +ZZZZ ZZZZ + + +ZZZ ZZZ + + +ZZ ZZ + C + +Z Z + + + + +ZZZZZZZZ +ZZZZZZZ +ZZZZZZ +ZZZZZ +ZZZZ +ZZZ +ZZ +Z +C +ZZ +ZZZ +ZZZZ +ZZZZZ +ZZZZZZ +ZZZZZZZ +ZZZZZZZZ + + + + + + + I + III + IIIII + III + I + + + I + III + IIIII + III + C + + + + + + + + + + + + SSSSSSSS +SSSS SSSSS +SSSSSSS S +SSSSSSS S + + S + S +SS SSSSSSS +SS SSSSSSS +SS SSSSSSS +SS SSCSSSS + + + + + JJJJ + J + J + J + JJJJ + J + J + J + JJJJ + J + J + J + JJJJ + C J + J + J + + + + + + + + + + + + +IIII IIII +III III +II II +I I +I C I +II II +III III +IIII IIII + + + + + + + + + + + + + SSSSSS + SS + S S + S S + C + + + + + + + + + + + + + + +ZZZZZZZZZ +SSSSSSSS +TTTTTTT +OOOOOO +IIIII +LLLL +JJJ +ZZ +C + + + +ZZZZZZZZ + + + ZZZZZZZZ + + +ZZZZZZZ + + + ZZZZZZZ + + +ZZZZCZZ + + + + + + + + + + + + + + + + +Z Z + Z Z + Z Z + Z Z C + Z Z + Z Z + Z Z +Z Z + + + + + + + + + + + + + + ZZZZZZZZZ +S SSSSSSSS +TT TTTTTTT +OOO OOOOOO +IIII IIIII +LLLLL LLLL +JJJJJC JJJ + + + + + + + +OO OO OO + + + + OO OO + + + +OO OO OO + + + C + + + + + +J J +JJ JJ +JJJ JJJ +JJJJ JJJJ +JJJ JJJ +JJ JJ +J J + C + +J J +JJ JJ +JJJ JJJ +JJJJ JJJJ +JJJ JJJ +JJ JJ +J J + + + + + SSSSS + S + S + SSSSS + S + S + SSSSS + S + S C + SSSSS + S + S + SSSSS + S + S + SSSSS + + + + OOOO + + + +OOOO OOO + + + + OOOO + + + +OOO OOOO + + C + + + + + + + IIIIII + II + II + II +ZZ II ZZ + II + II + II + IIIIII + II + CI + II +ZZ II ZZ + II + II + II + + + + + OO + OOOO + OOOOOO + + + TT + TTTT + TTTTTT + C + + SS + SSSS + SSSSSS + + + + + + + + + + + + + +S + S + S + S + S + S + S + S + S + S C + + + + + + + + + + + Z Z Z Z + + + Z Z Z Z + + +Z ZZZZZZZ +Z ZZZZZZC +Z ZZZZZZZ +Z ZZZZZZC + + + + + + + + + + + + + S S + S S S S +S SS S + + + S S + S S S S +S C SS C S + + + + + + + + + + T T + T T + T T + + TT + TT TT + T T + T T +T T + + C + + + + II + IIII + IIIIII + IIII + II + + + + + LL + LLLL + LLLLLL + LLLL + CL + + + + + + + + ZZZZZZ + Z Z + Z Z + Z Z + ZZZZZZ + Z Z + Z C Z + Z Z + ZZZZZZ + Z Z + Z Z + Z Z + ZZZZZZ + Z Z + Z Z + Z Z + + + + + + + + + + + I I + I I + I I I I + I I I I +I C II C I +I II I + I I I I + I I I I + I I + I I + + + + + + TTTTTT + + +TTTTTTT + TTTT + C C + + + TTTT + + +TTTT TTTT + TT TT + TT TT + TT C TTT + + + + + + + + + + ZZZZZZZZ + ZZ ZZ + ZZZZ + ZZ + + + + JJ + JJJJ + JJC JJ + JJJJJJJJ + + + + + + LLLLLL + L L + L C L + L L + L L + LLLLLL + + + + + + + + + + + + + + +L L +L L +L L + L L + L L + L L + L L + L L + L L + L L + L L + L L + CLL + LL + LL + + + + + + +ZZZZ ZZZZ +ZZZZ ZZZZ +ZZZ ZZZ +ZZZ ZZZ +ZZ ZZ +ZZ ZZ +Z C Z +Z C Z +ZZ ZZ +ZZ ZZ +ZZZ ZZZ +ZZZ ZZZ +ZZZZ ZZZZ +ZZZZ ZZZZ + + + + + + + LLLLLLLL + L + L + L + L + L + LL + LL + L + L + L C + L + L + LLLLLLLL + + + + + + + + + + + J JJJJ + J J J + J J J + J C J + J J J + J J J + JJJ J + + + + + + + + + + + + II + II II +II II + II II + II + + + II + II II +II II + II II +C II C + + + + + Z + ZZ + ZZZ + ZZZZ + ZZZZZ + ZZZZZZ + ZZZZZZZ + ZZZZZZZZ + ZZZZZZZZ + ZZZZZZZ + ZZZZZZ + ZZZCZ + ZZZZ + ZZZ + ZZ + Z + + + + +O +O + O + O + O + O + O + O + O + O + O + O + O + O + O +C O + + + + + + + T T + T T + + + T T + T T + + + T T + T T + + + T T + T C T + Z + Z Z + Z Z + Z Z + Z Z + Z Z + + + + ZZZZ + Z Z + Z Z + Z Z + ZC Z + Z Z + Z Z + Z Z + + + +C C + + + + +ZZZZZZZZZ + + ZZZZZZZZZ + +ZZZZZZZZZ + + ZZZZZZZZZ + +ZZZZZZZZZ + + ZZZZZZZZZ + +ZZZZZZZZZ + + ZZZZZZZZZ + + + + + +Z Z + Z Z + Z Z + Z Z + + ZC + ZZ ZZ + Z Z + Z Z +Z Z + + + + + + + + + + + + + S S +SCS SCS + S S + + + S S + SCS SCS + S S + + + S S +SCS SCS + S S + + + + + + + + + S T + S T + S T + S T + S S T T + S S T T + S S T T + S S T T +S ST T +S ST T +S C ST C T +S ST T + + + + + + ZZ + ZZ + ZZ + + ZZ + ZZ +ZZ + + ZZ + ZZ +C ZZ + + ZZ + ZZ +ZZ + + + + + + + + SSSSSSSS + S S + S SSSS S + S S S S + S S CS S + S S S S + S S SS S + S S S + S SSSSSS + + + + + + + +O O +O O +O O + O O + O O + O O + O O + O O + O O + O O + O O + O O C + + + + + + + + + + + + SS + SSSS + SSSSSS + SSSSSSSS + + + + SS + SSSS + SSSSSS + SSSSSSSS + C + + + + + + + ZZZZZZZZ + ZZ + ZZ + ZZ +SSS ZZ SSS + ZZ + ZZ + ZZ + ZZZZZZ + ZZ + ZZ + ZZ +SSS ZC SSS + ZZ + ZZ + ZZ + + + + + + + JJJJ + J J + J J + J J + J J + J J +J J +J C J + J J + J J + J J + J J + J J + JJJJ + + + + + + + +SSSSSSSSS + + SSSSSSSSS + +SSSSSSSSS + + SSSSSSSSS + +SSSSSSSSS + + SSSSSSSSS + C +SSSSSSSSS + + + + + + + TTTTTTTT + T + T + T + T + T + T + T + T + TTTTTTTT + C + + + + + + + + I I + + + I I I + + + I I + + +I I I + + + I I + + + C I I + + + + + L + L + LLL + LLL + LLLLL + LLLLL + I LLL + I LLL + III L + III L +IIIII +IIIII + III + III + I +C I + + + + + + + + + + +Z Z + Z Z + Z Z + Z Z + ZZ + ZZ + Z Z + Z Z + Z Z +Z C Z + + + + + + + + + + + ZZZZZZZZZ +S SSSSSSSS +TT TTTTTTT +OOO OOOOOO +IIII IIIII +LLLLL LLLL +JJJJJJ JJJ +ZZZZZZZ ZZ +SSSSSSSS S +CTTTTTTTT + + + + + + +Z Z +Z Z + Z Z + Z Z + Z Z + Z Z +C Z Z C + Z Z + Z Z + Z Z + Z Z + Z Z +Z Z +Z Z + + + + + II + II + II + II + I I + I I + I I + I I + I I + I I +I I +I I + C + + + + + + + + + ZZ Z + Z Z Z + Z Z Z + Z Z Z + Z Z Z + Z ZZ + Z Z + + + + + C + + + + Z + Z Z +C Z Z C + Z Z + Z Z + Z Z + Z Z + + + + + + + ZZ + + SSSS + + TTTTTT + + OOOOOOOO + + + + + + TTTTTTTT + TT + T T + T T + T T + + + + + + C + + + + + + + + + + + Z Z + Z Z + Z Z Z Z + Z Z Z Z +Z ZZ Z +Z C ZZ C Z + + + S S + S S + S S S S + S S S S +S SS S +S C SS C S + + + + + + + + +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S T O I +Z S C O I +Z S T O I + + + + + + ZZ + Z Z + Z Z + Z Z +Z Z + Z Z + Z Z + Z Z + ZZ + + + + + + C + + + + + +TTTTTTTTT + +TTTTTTT T + +TTTTT T T + +TTT T T T + +TTT T T T + +TTTTT T T + +CTTTTTT T + +TTTTTTTTT + + + + + SSSSS + S S + S S + S S S S + S S S S + S S S S + S S + S S + SSSSS + + + C + + + + + + + + + + Z Z + Z Z + Z Z + Z Z Z Z + Z Z Z Z + Z Z Z Z +Z ZZ Z +Z C ZZ C Z +Z ZZ Z + Z Z Z Z + Z Z Z Z + Z Z Z Z + Z Z + Z Z + Z Z + + + + + +ZZ ZZ + Z Z + Z Z + Z Z + Z Z + ZZ + ZZ + Z Z + Z Z + Z Z + Z Z + Z Z + Z Z +Z Z +Z C Z + + + + + + + + S S + S S + S S +S S S S + S S + S S +S S S S + S S + S S +S S S C + S S + S S +S S + + + + +ZZZZ + ZZZZ + + + ZZZZ + ZZZZ + + +ZZZZ + ZZZZ + + + ZZZZ + ZZZZ C + + + + + + + + + + + + + +Z Z Z Z Z + + S S S S S + +T T T T T + + O O O O O + +C I I I I +O O + O O + O O + O O + O O + O O + + + + +I I + I I +C I I + I I + I I + I I + + + + + + + + +S + S + S + S + S + S + S + S + S + S + S + S + S + C + S +S + + + + +Z +Z ZZZZZ +Z Z Z +Z ZCZ Z +Z ZZZ Z +Z Z +ZZZZZZZ + + + SSSSSSS + S S + S SSS S + S SCS S + S S S + SSSSS S + S + + + + + +Z S + Z S + Z S + Z S + ZS + TO + T O + T O + T O +T O + + + + + C + + + + + + + + Z Z S S + C C + Z Z S S + + +T T O O + C C +T T O O + + + I I L L + C C + I I L L + + + + + + + Z Z + Z Z + Z Z + Z Z +Z Z Z Z + Z Z + Z Z + Z Z + Z Z + Z Z + Z Z + + Z C Z + Z Z + + + + + ZZZ ZZZ + + ZZZ ZZZ + ZCZ ZCZ + ZZZ ZZZ + + ZZ + ZZ + ZZZZ + + + Z Z + Z Z + ZZZZ + + + + + + + + CC CCCC + C C C + CCCC C + + CCCCCCCCC + C C C + C C C + + CCCCCCCCC + C C C + CCCCC C + + CCCCCC + C CCC + CCCCCC +]] + +local function parseMap(str, map) + local array = {} + local start = 220 * (map - 1) + for row = 5, 24 do + local row_array = {} + for col = 1, 10 do + local num = start + 11 * (row - 5) + col + local char = str:sub(num, num) + local insert_this = nil + if char == "I" then + insert_this = { skin = "2tie", colour = "C" } + elseif char == "J" then + insert_this = { skin = "2tie", colour = "B" } + elseif char == "L" then + insert_this = { skin = "2tie", colour = "O" } + elseif char == "O" then + insert_this = { skin = "2tie", colour = "Y" } + elseif char == "S" then + insert_this = { skin = "2tie", colour = "G" } + elseif char == "T" then + insert_this = { skin = "2tie", colour = "M" } + elseif char == "Z" then + insert_this = { skin = "2tie", colour = "R" } + elseif char == "C" then + insert_this = { skin = "gem", colour = "F" } + end + row_array[col] = insert_this + end + array[row] = row_array + end + return array +end + +function FlashPointViewer:new() + self.super:new() + + self.ready_frames = 1 + self.next_queue_length = 0 + self.current_map = 1 + self.grid:applyMap(parseMap(board_str, 1)) +end + +function FlashPointViewer:advanceOneFrame(inputs, ruleset) + local prev_map = self.current_map + if inputs.right or (not self.prev_inputs.up and inputs.up) then + self.current_map = math.min(self.current_map + 1, 100) + elseif inputs.left or (not self.prev_inputs.down and inputs.down) then + self.current_map = math.max(self.current_map - 1, 1) + end + if prev_map ~= self.current_map then + self.grid:clear() + self.grid:applyMap(parseMap(board_str, self.current_map)) + end + self.prev_inputs = inputs + return false +end + +function FlashPointViewer:drawScoringInfo() + love.graphics.setColor(1, 1, 1, 1) + + love.graphics.setFont(font_3x5_3) + love.graphics.print("FLASHPOINT BOARD VIEWER", 240, 120) + love.graphics.print(self.current_map, 240, 200) + + love.graphics.setFont(font_3x5_2) + love.graphics.print("MAP", 240, 180) + love.graphics.print("UP/DOWN +/- 1\nLEFT/RIGHT FOR FAST SCROLL", 240, 260) +end + +return FlashPointViewer \ No newline at end of file diff --git a/tetris/modes/minesweeper.lua b/tetris/modes/minesweeper.lua new file mode 100644 index 0000000..9321b86 --- /dev/null +++ b/tetris/modes/minesweeper.lua @@ -0,0 +1,293 @@ +local GameMode = require 'tetris.modes.gamemode' +local Grid = require 'tetris.components.grid' + +local Minesweeper = GameMode:extend() + +Minesweeper.name = "Minesweeper" +Minesweeper.hash = "Minesweeper" +Minesweeper.tagline = "Avoid the exploding mines!" + +local width = 15 +local height = 15 + +function Minesweeper:new() + self.super:new() + self:initializeGrid() + self.menu_state = 1 + self.mines = {} + self.cursor = {x=1, y=1} + self.first_move = true + self.ready_frames = 1 + self.next_queue_length = 0 +end + +function Minesweeper:isMine(x, y) + for _, mine in pairs(self.mines) do + if mine.x == x and mine.y == y then + return true + end + end + return false +end + +function Minesweeper:initializeGrid() + self.grid = Grid(width, height + 4) + self.flags = math.floor(0.15 * width * height) + self.chosen_colour = ({ "R", "O", "Y", "G", "C", "B", "M" })[math.random(7)] + for y = 1, height do + for x = 1, width do + self.grid.grid[y+4][x] = { skin = "2tie", colour = self.chosen_colour } + end + end +end + +function Minesweeper:initializeMines(sel_x, sel_y) + for i = 1, self.flags do + local x, y + repeat + x = math.random(1, width) + y = math.random(1, height) + until not (self:isMine(x, y) or (sel_x == x and sel_y == y)) + table.insert(self.mines, {x=x, y=y}) + end +end + +function Minesweeper:advanceOneFrame(inputs, ruleset) + if self.menu_state ~= 0 then + self:menuLoop(inputs) + return false + elseif self.game_over or self.completed or self.ready_frames > 1 then + return true + else + self:mainGameLoop(inputs) + return false + end +end + +function Minesweeper:menuLoop(inputs) + if not self.prev_inputs.right and inputs.right then + if self.menu_state == 1 then + width = math.min(width + 1, 28) + else + height = math.min(height + 1, 22) + end + self:initializeGrid() + elseif not self.prev_inputs.left and inputs.left then + if self.menu_state == 1 then + width = math.max(width - 1, 8) + else + height = math.max(height - 1, 8) + end + self:initializeGrid() + elseif (not self.prev_inputs.down and inputs.down) or + (not self.prev_inputs.up and inputs.up) then + self.menu_state = -self.menu_state + end + if not self.prev_inputs.rotate_left and inputs.rotate_left then + self.ready_frames = 100 + self.menu_state = 0 + end + self.prev_inputs = inputs +end + +function Minesweeper:mainGameLoop(inputs) + self:moveCursor(inputs) + if not self.prev_inputs.rotate_left and inputs.rotate_left and + self.grid.grid[self.cursor.y+4][self.cursor.x].colour ~= "F" and + self.grid.grid[self.cursor.y+4][self.cursor.x].colour ~= "A" then + playSE("lock") + if not self:isMine(self.cursor.x, self.cursor.y) then + self:uncoverCell(self.cursor.x, self.cursor.y) + else + self.game_over = true + end + end + if not self.prev_inputs.rotate_right and inputs.rotate_right then + self:flagCell(self.cursor.x, self.cursor.y) + end + self:updateFlagCount() + self.frames = self.frames + 1 + self.prev_inputs = inputs +end + +function Minesweeper:updateFlagCount() + local flags = math.floor(0.15 * width * height) + for y = 5, height + 4 do + for x = 1, width do + if self.grid.grid[y][x].colour == "A" then + flags = flags - 1 + end + end + end + self.flags = flags +end + +function Minesweeper:moveCursor(inputs) + if not self.prev_inputs.right and inputs.right then + self.cursor.x = math.min(self.cursor.x + 1, width) + elseif not self.prev_inputs.left and inputs.left then + self.cursor.x = math.max(self.cursor.x - 1, 1) + elseif not self.prev_inputs.down and inputs.down then + self.cursor.y = math.min(self.cursor.y + 1, height) + elseif not self.prev_inputs.up and inputs.up then + self.cursor.y = math.max(self.cursor.y - 1, 1) + end +end + +function Minesweeper:flagCell(x, y) + if self.flags > 0 and self.grid.grid[y+4][x].skin == "2tie" and + self.grid.grid[y+4][x].colour ~= "F" then + self.grid.grid[y+4][x] = { + skin = "gem", colour = "A" + } + elseif self.grid.grid[y+4][x].skin == "gem" then + self.grid.grid[y+4][x] = { + skin = "2tie", colour = self.chosen_colour + } + end +end + +function Minesweeper:uncoverCell(x, y) + if self.first_move then + self:initializeMines(x, y) + self.first_move = false + end + + local stack = { + { + x = x, + y = y, + } + } + + while #stack > 0 do + local current = table.remove(stack) + local x = current.x + local y = current.y + + self.grid.grid[y+4][x] = { skin = "2tie", colour = "F" } + + if self:getSurroundingMineCount(x, y) == 0 then + for dy = -1, 1 do + for dx = -1, 1 do + if not (dx == 0 and dy == 0) and + x+dx >= 1 and x+dx <= width and + y+dy >= 1 and y+dy <= height and + self.grid.grid[y+4+dy][x+dx].colour ~= "F" + then + table.insert(stack, { + x = x + dx, + y = y + dy, + }) + end + end + end + end + end + + for y = 5, height + 4 do + for x = 1, width do + if self.grid.grid[y][x].colour ~= "F" and + not self:isMine(x, y-4) then + return + end + end + end + + self.completed = true +end + +function Minesweeper:getSurroundingMineCount(x, y) + local mines = 0 + for dy = -1, 1 do + for dx = -1, 1 do + if not (dx == 0 and dy == 0) and self:isMine(x+dx, y+dy) then + mines = mines + 1 + end + end + end + return mines +end + +function Minesweeper:onGameOver() + for y = 5, height + 4 do + for x = 1, width do + if self:isMine(x, y-4) then + self.grid.grid[y][x] = { skin = "gem", colour = "R" } + else + self.grid.grid[y][x] = { skin = "2tie", colour = "F" } + end + end + end +end + +function Minesweeper:onGameComplete() + self:onGameOver() + love.graphics.setColor( + self.game_over_frames % 4 < 2 and + {0.3, 1, 0.3, 1} or + {1, 1, 1, 1} + ) + love.graphics.print("GOOD JOB!", 80 + 16 * width, 280) +end + +local function opacityFunction(game, block, x, y, age) + if block.colour == "F" then + return 0.5, 0.5, 0.5, 1, 1 + else + return 1, 1, 1, 1, 1 + end +end + +function Minesweeper:drawGrid() + self.grid:drawCustom(opacityFunction, self) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setFont(font_3x5_2) + love.graphics.setLineWidth(2) + for y = 5, height + 4 do + for x = 1, width do + if self.cursor.x == x and self.cursor.y + 4 == y then + love.graphics.rectangle("line", 48+x*16, y*16, 16, 16) + end + if self:getSurroundingMineCount(x, y-4) ~= 0 and + self.grid.grid[y][x].colour == "F" then + love.graphics.print(self:getSurroundingMineCount(x, y-4), 50+x*16, -2+y*16) + end + end + end +end + +function Minesweeper:drawScoringInfo() + love.graphics.setColor(1, 1, 1, 1) + + local x = 80 + 16 * width + love.graphics.setFont(font_3x5_2) + love.graphics.print("DIMENSIONS", x, 100) + love.graphics.print("FLAGS", x, 160) + love.graphics.print("TIME ELAPSED", x, 220) + love.graphics.setFont(font_3x5_3) + love.graphics.print(width .. " x " .. height, x, 120) + love.graphics.print(self.flags, x, 180) + love.graphics.print(formatTime(self.frames), x, 240) +end + +function Minesweeper:drawCustom() + if self.menu_state ~= 0 then + love.graphics.setFont(font_3x5_3) + love.graphics.setColor( + self.menu_state == 1 and + {1, 1, 0.3, 1} or + {1, 1, 1, 1} + ) + love.graphics.print("WIDTH: " .. width, 67, 100) + love.graphics.setColor( + self.menu_state == -1 and + {1, 1, 0.3, 1} or + {1, 1, 1, 1} + ) + love.graphics.print("HEIGHT: " .. height, 67, 140) + love.graphics.setColor(1, 1, 1, 1) + end +end + +return Minesweeper \ No newline at end of file diff --git a/tetris/modes/snake.lua b/tetris/modes/snake.lua new file mode 100644 index 0000000..ab0c260 --- /dev/null +++ b/tetris/modes/snake.lua @@ -0,0 +1,138 @@ +local GameMode = require 'tetris.modes.gamemode' +local Grid = require 'tetris.components.grid' + +local Snake = GameMode:extend() + +Snake.name = "Snake" +Snake.hash = "Snake" +Snake.tagline = "...It's literally just snake." + +local moves = { + ["right"] = {x=1, y=0}, + ["left"] = {x=-1, y=0}, + ["up"] = {x=0, y=-1}, + ["down"] = {x=0, y=1}, +} + +function Snake:new() + self.super:new() + self.grid = Grid(25, 24) + self.next_queue_length = 0 + self.ready_frames = 1 + self.ticks = 60 + self.direction = "right" + self.last_move = "right" + self.snake = { + {x=11, y=15}, + {x=12, y=15}, + {x=13, y=15}, + {x=14, y=15}, + } + self.gem = {x=1, y=5} + self:generateGem() + self:updateGrid() +end + +function Snake:isInSnake(p) + for _, point in pairs(self.snake) do + if point.x == p.x and point.y == p.y then + return true + end + end + return false +end + +function Snake:updateGrid() + local snake = { skin = "2tie", colour = "G" } + local gem = { skin = "gem", colour = "R" } + self.grid:clear() + self.grid.grid[self.gem.y][self.gem.x] = gem + for y = 5, 24 do + for x = 1, 25 do + if self:isInSnake({x=x, y=y}) then + self.grid.grid[y][x] = snake + end + self.grid.grid_age[y][x] = 3 + end + end +end + +function Snake:generateGem() + repeat + self.gem.x = math.random(1, 25) + self.gem.y = math.random(5, 24) + until not self:isInSnake({x=self.gem.x, y=self.gem.y}) +end + +function Snake:advanceOneFrame(inputs, ruleset) + if self.game_over or self.completed then return true end + if self.last_move ~= "left" and not self.prev_inputs.right and inputs.right then + self.direction = "right" + elseif self.last_move ~= "right" and not self.prev_inputs.left and inputs.left then + self.direction = "left" + elseif self.last_move ~= "down" and not self.prev_inputs.up and inputs.up then + self.direction = "up" + elseif self.last_move ~= "up" and not self.prev_inputs.down and inputs.down then + self.direction = "down" + end + self.ticks = self.ticks - 1 + if self.ticks <= 0 then + local move = moves[self.direction] + local new_x = self.snake[#self.snake].x + move.x + local new_y = self.snake[#self.snake].y + move.y + if ( + new_x < 1 or new_x > 25 or new_y < 5 or new_y > 24 or + self:isInSnake({x=new_x, y=new_y}) + ) then + self.game_over = true + else + table.insert(self.snake, { + x = new_x, + y = new_y, + }) + if new_x == self.gem.x and new_y == self.gem.y then + if #self.snake >= 500 then + self.completed = true + self:updateGrid() + return false + else + self:generateGem() + end + else + table.remove(self.snake, 1) + end + self:updateGrid() + self.last_move = self.direction + self.ticks = 10 + end + end + self.prev_inputs = inputs + return false +end + +local function noOutline(game, block, x, y, age) + local x = 0.75 + return x, x, x, 1, 0 +end + +function Snake:drawGrid() + self.grid:drawCustom(noOutline, self) +end + +function Snake:drawScoringInfo() + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setFont(font_3x5_2) + love.graphics.print("SNAKE LENGTH", 480, 130) + love.graphics.print("GEM LOCATION", 480, 210) + love.graphics.setFont(font_3x5_3) + love.graphics.print(#self.snake, 480, 150) + love.graphics.print(self.gem.x .. ", " .. self.gem.y - 4, 480, 230) +end + +function Snake:getHighscoreData() + return { + snake_length = #self.snake, + } +end + +return Snake \ No newline at end of file diff --git a/tetris/modes/stacker.lua b/tetris/modes/stacker.lua new file mode 100644 index 0000000..28b5fc2 --- /dev/null +++ b/tetris/modes/stacker.lua @@ -0,0 +1,143 @@ +local GameMode = require 'tetris.modes.gamemode' +local Grid = require 'tetris.components.grid' + +local StackerGame = GameMode:extend() + +StackerGame.name = "Stacker" +StackerGame.hash = "Stacker" +StackerGame.tagline = "Hey, wait, I didn't mean this kind of block stacking!" + +local block = { skin = "2tie", colour = "C" } + +function StackerGame:new() + self.super:new() + self.grid = Grid(7, 19) + self.next_queue_length = 0 + self.ready_frames = 1 + self.row = 19 + self.block_width = 3 + self.position = 0 + self.direction = 1 + self.ticks = 0 + self.are = 30 + self.map = {} + block.colour = "C" + self:updateGrid() +end + +function StackerGame:getSpeed() + return 20 ^ ((self.row - 4) / 15) +end + +function StackerGame:getMaxBlockWidth() + if self.row >= 17 then return 3 + elseif self.row >= 11 then return 2 + else return 1 end +end + +function StackerGame:updateGrid() + local width = math.min(self:getMaxBlockWidth(), self.block_width) + self.grid:clear() + self.grid:applyMap(self.map) + for w = 0, width - 1 do + self.grid.grid[self.row][self.position - w] = block + end + for y = 5, 19 do + for x = 1, 7 do + self.grid.grid_age[y][x] = 3 + end + end +end + +function StackerGame:advanceOneFrame(inputs, ruleset) + if self.game_over or self.completed then return true end + if self.are > 0 then + self.are = self.are - 1 + return false + end + if not self.prev_inputs.up and inputs.up then + self.prev_inputs = inputs + local width = math.min(self:getMaxBlockWidth(), self.block_width) + local new_width = 0 + local row = {} + for w = 0, width - 1 do + if self.grid:isOccupied(self.position - w - 1, self.row) and + self.position - w >= 1 and self.position - w <= 7 then + new_width = new_width + 1 + row[self.position - w] = block + end + end + if new_width == 0 then + self.game_over = true + else + playSE("lock") + self.map[self.row] = row + self.row = self.row - 1 + if self.row <= 8 and self.row >= 5 then + block.colour = "R" + elseif self.row <= 4 then + block.colour = "G" + self.completed = true + self:updateGrid() + return false + end + self.ticks = 0 + self.block_width = new_width + self.position = 0 + self.are = 30 + self:updateGrid() + end + return false + end + self.ticks = self.ticks + 1 + if self.ticks >= self:getSpeed() then + self.ticks = self.ticks - self:getSpeed() + if self.position >= 7 + math.min(self:getMaxBlockWidth(), self.block_width) - 1 then + self.direction = -1 + elseif self.position <= 1 then + self.direction = 1 + end + self.position = self.position + self.direction + self:updateGrid() + end + self.prev_inputs = inputs + return false +end + +local function noOutline(game, block, x, y, age) + local x = 0.75 + return x, x, x, 1, 0 +end + +function StackerGame:onGameComplete() end + +function StackerGame:drawGrid() + self.grid:drawCustom(noOutline, self) +end + +function StackerGame:drawScoringInfo() + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setFont(font_3x5_2) + love.graphics.print("<-- MAJOR PRIZE", 190, 80) + love.graphics.print("<-- MINOR PRIZE", 190, 144) + love.graphics.print("ROWS STACKED", 64, 340) + love.graphics.print("FRAMES PER MOVE", 64, 400) + love.graphics.setFont(font_3x5_3) + love.graphics.print(19 - self.row .. (self.row <= 4 and " - COMPLETE!" or ""), 64, 360) + love.graphics.print( + ( + self.row > 4 and + string.format("%.02f", self:getSpeed()) or + "N/A" + ), + 64, 420 + ) +end + +function StackerGame:getHighscoreData() + return { + rows = 19 - self.row, + } +end + +return StackerGame \ No newline at end of file diff --git a/tetris/rulesets/asc.lua b/tetris/rulesets/asc.lua new file mode 100644 index 0000000..6804546 --- /dev/null +++ b/tetris/rulesets/asc.lua @@ -0,0 +1,120 @@ +local Ruleset = require 'tetris.rulesets.ruleset' + +local Ascension = Ruleset:extend() + +Ascension.name = "Ascension" +Ascension.hash = "Ascension" +Ascension.world = true +Ascension.colourscheme = { + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", +} +Ascension.softdrop_lock = false +Ascension.harddrop_lock = true + +Ascension.spawn_positions = { + I = { x=4, y=4 }, + J = { x=4, y=5 }, + L = { x=4, y=5 }, + O = { x=4, y=5 }, + S = { x=4, y=5 }, + T = { x=4, y=5 }, + Z = { x=4, y=5 }, +} + +Ascension.big_spawn_positions = { + I = { x=2, y=2 }, + J = { x=2, y=3 }, + L = { x=2, y=3 }, + O = { x=2, y=3 }, + S = { x=2, y=3 }, + T = { x=2, y=3 }, + Z = { x=2, y=3 }, +} + +Ascension.block_offsets = { + I={ + { {x=-1, y=0}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} }, + { {x=0, y=-1}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} }, + { {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} }, + { {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0}, {x=0, y=1} }, + }, + J={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} }, + { {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} }, + { {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} }, + }, + L={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} }, + { {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} }, + { {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} }, + }, + O={ + { {x=0, y=-1}, {x=1, y=-1}, {x=0, y=0}, {x=1, y=0} }, + { {x=0, y=0}, {x=1, y=0}, {x=0, y=1}, {x=1, y=1} }, + { {x=-1, y=0}, {x=0, y=0}, {x=-1, y=1}, {x=0, y=1} }, + { {x=-1, y=-1}, {x=0, y=-1}, {x=-1, y=0}, {x=0, y=0} }, + }, + S={ + { {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} }, + { {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} }, + { {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} }, + { {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} }, + }, + T={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} }, + { {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} }, + { {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} }, + }, + Z={ + { {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} }, + { {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} }, + { {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} }, + { {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} }, + } +} + +-- CCW kicks, negative X for CW +Ascension.kicks = { + {x=1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=0, y=2}, {x=1, y=2}, {x=2, y=0}, {x=2, y=1}, + {x=2, y=2}, {x=-1, y=0}, {x=-1, y=1}, {x=0, y=-1}, {x=1, y=-1}, {x=2, y=-1}, {x=-1, y=2}, + {x=-2, y=0}, {x=0, y=-2}, {x=1, y=-2}, {x=2, y=-2}, {x=-2, y=1}, {x=-2, y=2}, {x=-1, y=-1}, +} + +function Ascension:attemptWallkicks(piece, new_piece, rot_dir, grid) + + if rot_dir == 2 then return end + + local kicks = Ascension.kicks + + assert(piece.rotation ~= new_piece.rotation) + + for idx, o in pairs(kicks) do + offset = { + x = o.x * (rot_dir == 1 and -1 or 1), + y = o.y + } + kicked_piece = new_piece:withOffset(offset) + if grid:canPlacePiece(kicked_piece) then + self:onPieceRotate(piece, grid) + piece:setRelativeRotation(rot_dir) + piece:setOffset(offset) + return + end + end + +end + +function Ascension:onPieceDrop(piece) piece.lock_delay = 0 end +function Ascension:onPieceMove(piece) piece.lock_delay = 0 end +function Ascension:onPieceRotate(piece) piece.lock_delay = 0 end + +return Ascension \ No newline at end of file