diff --git a/.gitignore b/.gitignore index 1d4ff14..6fa5638 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ skins/donotupload/* ss ss/* *.sav -res/bgm/track* \ No newline at end of file +res/bgm/track* +replays +replays/* \ No newline at end of file diff --git a/tetris/modes/baboo.lua b/tetris/modes/baboo.lua index 4659e8d..6aab3ef 100644 --- a/tetris/modes/baboo.lua +++ b/tetris/modes/baboo.lua @@ -1,4 +1,5 @@ local GameMode = require 'tetris.modes.gamemode' +local Bag7Randomizer = require 'tetris.randomizers.bag7' local MarathonWBGame = GameMode:extend() @@ -15,6 +16,7 @@ function MarathonWBGame:new() self.instant_soft_drop = false self.enable_hold = true self.next_queue_length = 6 + self.randomizer = Bag7Randomizer() self.keystrokes = 0 self.pieces = 0 diff --git a/tetris/modes/duality_a1.lua b/tetris/modes/duality_a1.lua index 3a84f64..c0247e0 100644 --- a/tetris/modes/duality_a1.lua +++ b/tetris/modes/duality_a1.lua @@ -41,13 +41,13 @@ function DualityA1Game:afterLineClear(cleared_row_count) end local function fadeOut(game, block, x, y, age) - local opacity = game.are / (game:getARE() / 2) - 1 + local opacity = 2 * game.are / game:getARE() - 1 return 0.5, 0.5, 0.5, opacity, opacity end local function fadeIn(game, block, x, y, age) - local opacity = game.are / (game:getARE() / 2) - 1 - return 0.5, 0.5, 0.5, 1 - opacity, 1 - opacity + local opacity = 1 - (2 * game.are / game:getARE()) + return 0.5, 0.5, 0.5, opacity, opacity end function DualityA1Game:drawGrid() @@ -62,4 +62,4 @@ function DualityA1Game:drawGrid() end end -return DualityA1Game \ No newline at end of file +return DualityA1Game diff --git a/tetris/modes/lj.lua b/tetris/modes/lj.lua index 224d991..b1792f1 100644 --- a/tetris/modes/lj.lua +++ b/tetris/modes/lj.lua @@ -1,4 +1,5 @@ local GameMode = require 'tetris.modes.gamemode' +local Bag7Randomizer = require 'tetris.randomizers.bag7' local MarathonWLJGame = GameMode:extend() @@ -15,6 +16,7 @@ function MarathonWLJGame:new() self.instant_soft_drop = false self.enable_hold = true self.next_queue_length = 6 + self.randomizer = Bag7Randomizer() self.pieces = 0 self.b2b = false diff --git a/tetris/modes/marathon_c88.lua b/tetris/modes/marathon_c88.lua index 5eb24ed..94e8315 100644 --- a/tetris/modes/marathon_c88.lua +++ b/tetris/modes/marathon_c88.lua @@ -40,8 +40,7 @@ function MarathonC88Game:new(secret_inputs) self.irs = false self.grid.getCell = function(self, x, y) - if x < 1 or x > self.width or y < 5 or y > self.height then return oob - elseif y < 1 then return empty + if x < 1 or x > self.width or y < 5 or y > self.height then return nil else return self.grid[y][x] end end diff --git a/tetris/modes/marathon_st.lua b/tetris/modes/marathon_st.lua index 6c09403..2a80b86 100644 --- a/tetris/modes/marathon_st.lua +++ b/tetris/modes/marathon_st.lua @@ -124,7 +124,7 @@ function MarathonSTGame:advanceOneFrame() end return false end - if self.roll_frames > 3694 then + if self.roll_frames > 3701 then switchBGM(nil) self.completed = true end diff --git a/tetris/modes/speed.lua b/tetris/modes/speed.lua index c4ce28f..c382b25 100644 --- a/tetris/modes/speed.lua +++ b/tetris/modes/speed.lua @@ -14,9 +14,9 @@ function LudicrousSpeed:new() self.super:new() self.time_limit = 300 - self.pps = {} + self.delays = {} + self.last_piece = 0 self.pps_limit = 1 - self.pieces = 0 self.randomizer = Bag7Randomizer() @@ -28,6 +28,16 @@ function LudicrousSpeed:new() self.next_queue_length = 6 end +local function mean(t) + local sum = 0 + + for _, v in pairs(t) do + sum = sum + v + end + + return sum / #t +end + function LudicrousSpeed:getGravity() if self.lines < 180 then return (0.8 - (math.floor(self.lines / 10) * 0.007)) ^ -math.floor(self.lines / 10) / 60 @@ -42,41 +52,31 @@ function LudicrousSpeed:getARR() return config.arr end function LudicrousSpeed:getDasCutDelay() return config.dcd end function LudicrousSpeed:getDropSpeed() return 20 end -local function mean(t) - local sum = 0 - local count = 0 - - for k, v in pairs(t) do - if type(v) == 'number' then - sum = sum + v - count = count + 1 - end - end - - return (sum / count) +function LudicrousSpeed:getPPS() + if #self.delays == 0 then return 0 end + local delays = copy(self.delays) + delays[0] = self.frames - self.last_piece + return 60 / mean(delays) end function LudicrousSpeed:advanceOneFrame() if self.ready_frames == 0 then self.frames = self.frames + 1 - if mean(self.pps) * 60 < self.pps_limit then + if self:getPPS() < self.pps_limit then self.time_limit = self.time_limit - 1 self.game_over = self.time_limit <= 0 else self.time_limit = 300 end - if self.frames ~= 0 then - if table.getn(self.pps) == 750 then - table.remove(self.pps, 1) - table.insert(self.pps, self.pieces) - else table.insert(self.pps, self.pieces) end - self.pieces = 0 - end end return true end -function LudicrousSpeed:onPieceLock() - self.super:onPieceLock() - self.pieces = self.pieces + 1 +function LudicrousSpeed:onPieceLock(...) + self.super:onPieceLock(...) + self.delays[#self.delays + 1] = self.frames - self.last_piece + if #self.delays >= 25 then + table.remove(self.delays, 1) + end + self.last_piece = self.frames end function LudicrousSpeed:onLineClear(cleared_row_count) @@ -104,6 +104,13 @@ function LudicrousSpeed:drawScoringInfo() love.graphics.printf("LINES", text_x, 120, 80, "left") love.graphics.printf("piece/sec", text_x, 180, 80, "left") love.graphics.printf("REQUIRED PPS", text_x, 240, 120, "left") + if self.time_limit < 300 then + if self.time_limit % 4 < 2 and self.time_limit ~= 0 then + love.graphics.setColor(1, 0.3, 0.3, 1) + end + love.graphics.printf("SPEED UP!", text_x, 300, 120, "left") + love.graphics.setColor(1, 1, 1, 1) + end love.graphics.print( self.das.direction .. " " .. @@ -114,13 +121,13 @@ function LudicrousSpeed:drawScoringInfo() love.graphics.setFont(font_3x5_4) if self.time_limit < 300 then - love.graphics.printf(formatTime(self.time_limit), text_x, 320, 160, "left") + love.graphics.printf(formatTime(self.time_limit):sub(-4, -1), text_x, 320, 160, "left") end love.graphics.setFont(font_3x5_3) love.graphics.printf(self.lines, text_x, 140, 80, "left") - love.graphics.printf(string.format("%.02f", self.frames > 0 and mean(self.pps) * 60 or 0), text_x, 200, 80, "left") - love.graphics.printf(string.format("%.02f", self.pps_limit), text_x, 260, 80, "left") + love.graphics.printf(("%.02f"):format(self:getPPS()), text_x, 200, 80, "left") + love.graphics.printf(("%.02f"):format(self.pps_limit), text_x, 260, 80, "left") love.graphics.setFont(font_8x11) love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") @@ -137,4 +144,4 @@ function LudicrousSpeed:getHighscoreData() } end -return LudicrousSpeed \ No newline at end of file +return LudicrousSpeed diff --git a/tetris/modes/survival_gte.lua b/tetris/modes/survival_gte.lua index 0818ae7..d3eaf0f 100644 --- a/tetris/modes/survival_gte.lua +++ b/tetris/modes/survival_gte.lua @@ -56,6 +56,25 @@ function SurvivalGTEGame:onLineClear(cleared_row_count) self.completed = self.lines >= 300 end +local function getLowestBlockY(offsets) + local res = -math.huge + for _, o in pairs(offsets) do + if o.y > res then + res = o.y + end + end + return res +end + +function SurvivalGTEGame:onEnterOrHold(...) + while ( + getLowestBlockY(self.piece:getBlockOffsets()) + self.piece.position.y + ) < 4 do + self.piece.position.y = self.piece.position.y + 1 + end + self.super.onEnterOrHold(self, ...) +end + function SurvivalGTEGame:advanceOneFrame() if self.ready_frames == 0 then self.frames = self.frames + 1 diff --git a/tetris/rulesets/bliss.lua b/tetris/rulesets/bliss.lua new file mode 100644 index 0000000..14501d1 --- /dev/null +++ b/tetris/rulesets/bliss.lua @@ -0,0 +1,170 @@ +local Piece = require 'tetris.components.piece' +local Ruleset = require 'tetris.rulesets.ruleset' + +local BlissRS = Ruleset:extend() + +BlissRS.name = "Blissful" +BlissRS.hash = "Bliss" + +BlissRS.world = true +BlissRS.spawn_above_field = true +BlissRS.softdrop_lock = false +BlissRS.harddrop_lock = true +BlissRS.colourscheme = { + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", +} + +BlissRS.spawn_positions = { + I = { x=5, y=5 }, + J = { x=4, y=5 }, + L = { x=4, y=5 }, + O = { x=5, y=5 }, + S = { x=4, y=5 }, + T = { x=4, y=5 }, + Z = { x=4, y=5 }, +} + +BlissRS.big_spawn_positions = { + I = { x=3, y=2 }, + J = { x=2, y=3 }, + L = { x=2, y=3 }, + O = { x=3, y=3 }, + S = { x=2, y=3 }, + T = { x=2, y=3 }, + Z = { x=2, y=3 }, +} + +BlissRS.block_offsets = { + I={ + { {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} }, + { {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=0, y=1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} }, + { {x=0, y=-1}, {x=0, y=-2}, {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=-1}, {x=1, y=-2}, {x=0, y=-2}, {x=0, y=0} }, + { {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0} }, + { {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=-1, y=0} }, + }, + L={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} }, + { {x=0, y=-2}, {x=0, y=-1}, {x=1, y=0}, {x=0, y=0} }, + { {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0} }, + { {x=0, y=-1}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=0} }, + }, + O={ + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} }, + }, + S={ + { {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} }, + { {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} }, + { {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} }, + { {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} }, + }, + T={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} }, + { {x=0, y=-1}, {x=0, y=0}, {x=1, y=-1}, {x=0, y=-2} }, + { {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=0} }, + { {x=0, y=-1}, {x=0, y=0}, {x=-1, y=-1}, {x=0, y=-2} }, + }, + Z={ + { {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} }, + { {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} }, + { {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} }, + { {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} }, + } +} + +BlissRS.wallkicks_3x3 = { + [0]={ + [1]={{x=0, y=1}, {x=1, y=0}, {x=-1, y=0}}, + [2]={{x=0, y=1}}, + [3]={{x=0, y=1}, {x=-1, y=0}, {x=1, y=0}}, + }, + [1]={ + [0]={{x=0, y=1}, {x=-1, y=0}, {x=1, y=0}}, + [2]={{x=0, y=1}, {x=1, y=0}, {x=-1, y=0}}, + [3]={{x=1, y=0}}, + }, + [2]={ + [0]={{x=0, y=1}}, + [1]={{x=0, y=1}, {x=-1, y=0}, {x=1, y=0}}, + [3]={{x=0, y=1}, {x=1, y=0}, {x=-1, y=0}}, + }, + [3]={ + [0]={{x=0, y=1}, {x=1, y=0}, {x=-1, y=0}}, + [1]={{x=-1, y=0}}, + [2]={{x=0, y=1}, {x=-1, y=0}, {x=1, y=0}}, + } +} + +BlissRS.wallkicks_line = { + [0]={ + [1]={{x=0, y=1}, {x=0, y=-1}, {x=-1, y=1}}, + [2]={{x=0, y=0}}, + [3]={{x=0, y=1}, {x=-1, y=1}, {x=0, y=-1}}, + }, + [1]={ + [0]={{x=0, y=1}, {x=1, y=1}, {x=-1, y=0}, {x=1, y=0}, {x=2, y=0}}, + [2]={{x=0, y=1}, {x=1, y=1}, {x=1, y=0}, {x=-1, y=0}, {x=2, y=0}}, + [3]={{x=0, y=0}}, + }, + [2]={ + [1]={{x=0, y=1}, {x=-1, y=1}, {x=0, y=-1}}, + [2]={{x=0, y=0}}, + [3]={{x=0, y=1}, {x=0, y=-1}, {x=-1, y=1}}, + }, + [3]={ + [0]={{x=0, y=1}, {x=1, y=1}, {x=-1, y=0}, {x=1, y=0}, {x=2, y=0}}, + [2]={{x=0, y=1}, {x=1, y=1}, {x=1, y=0}, {x=-1, y=0}, {x=2, y=0}}, + [3]={{x=0, y=0}}, + } +} + +-- Component functions. + +function BlissRS:attemptWallkicks(piece, new_piece, rot_dir, grid) + + local kicks + if piece.shape == "O" then + return + elseif piece.shape == "I" then + kicks = BlissRS.wallkicks_line[piece.rotation][new_piece.rotation] + else + kicks = BlissRS.wallkicks_3x3[piece.rotation][new_piece.rotation] + end + + assert(piece.rotation ~= new_piece.rotation) + + for idx, offset in pairs(kicks) do + kicked_piece = new_piece:withOffset(offset) + if grid:canPlacePiece(kicked_piece) then + piece:setRelativeRotation(rot_dir) + piece:setOffset(offset) + self:onPieceRotate(piece, grid) + return + end + end +end + +function BlissRS:onPieceDrop(piece, grid) + piece.lock_delay = 0 -- step reset +end + +function BlissRS:getAboveFieldOffset() + return 1 +end + +function BlissRS:get180RotationValue() return 2 end + +return BlissRS \ No newline at end of file diff --git a/tetris/rulesets/nintendo_r.lua b/tetris/rulesets/nintendo_r.lua index 4e59353..df90856 100644 --- a/tetris/rulesets/nintendo_r.lua +++ b/tetris/rulesets/nintendo_r.lua @@ -7,12 +7,12 @@ Nintendo.hash = "NintendoR" Nintendo.spawn_positions = { I = { x=5, y=4 }, - J = { x=4, y=5 }, - L = { x=4, y=5 }, + J = { x=5, y=5 }, + L = { x=5, y=5 }, O = { x=5, y=5 }, - S = { x=4, y=5 }, - T = { x=4, y=5 }, - Z = { x=4, y=5 }, + S = { x=5, y=5 }, + T = { x=5, y=5 }, + Z = { x=5, y=5 }, } Nintendo.big_spawn_positions = { diff --git a/tetris/rulesets/trans.lua b/tetris/rulesets/trans.lua index 33e86e9..fe45433 100644 --- a/tetris/rulesets/trans.lua +++ b/tetris/rulesets/trans.lua @@ -21,28 +21,34 @@ function Trans:attemptRotate(new_inputs, piece, grid, initial) end local new_piece = piece:withRelativeRotation(rot_dir) + + if initial then + if (grid:canPlacePiece(new_piece)) then + self:onPieceRotate(piece, grid) + piece:setRelativeRotation(rot_dir) + end + else + self:attemptWallkicks(piece, new_piece, rot_dir, grid) + end +end + +function Trans:attemptWallkicks(piece, new_piece, rot_dir, grid) local pieces = {"I", "J", "L", "O", "S", "T", "Z"} repeat new_piece.shape = pieces[math.random(7)] until piece.shape ~= new_piece.shape - if (grid:canPlacePiece(new_piece)) then - self:onPieceRotate(piece, grid) - piece:setRelativeRotation(rot_dir) - piece.shape = new_piece.shape - else - if not(initial and self.enable_IRS_wallkicks == false) then - if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then - self:onPieceRotate(piece, grid) - piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0}) - piece.shape = new_piece.shape - elseif (grid:canPlacePiece(new_piece:withOffset({x=-1, y=0}))) then - self:onPieceRotate(piece, grid) - piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) - piece.shape = new_piece.shape - end - end - end + local offsets = {{x=0, y=0}, {x=1, y=0}, {x=-1, y=0}} + for _, o in pairs(offsets) do + local kicked_piece = new_piece:withOffset(o) + if (grid:canPlacePiece(kicked_piece)) then + self:onPieceRotate(piece, grid) + piece:setRelativeRotation(rot_dir) + piece:setOffset(o) + piece.shape = new_piece.shape + return + end + end end return Trans \ No newline at end of file