diff --git a/tetris/components/piece.lua b/tetris/components/piece.lua index 15c0a76..5ea5cd4 100644 --- a/tetris/components/piece.lua +++ b/tetris/components/piece.lua @@ -98,7 +98,7 @@ end function Piece:dropToBottom(grid) local piece_y = self.position.y - self:dropSquares(24, grid) + self:dropSquares(math.huge, grid) self.gravity = 0 if self.position.y > piece_y then -- if it got dropped any, also reset lock delay diff --git a/tetris/modes/marathon_a3.lua b/tetris/modes/marathon_a3.lua index 7470955..3b0d68a 100644 --- a/tetris/modes/marathon_a3.lua +++ b/tetris/modes/marathon_a3.lua @@ -17,7 +17,7 @@ MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all function MarathonA3Game:new() MarathonA3Game.super:new() - self.speed_level = 0 + self.speed_level = 0 self.roll_frames = 0 self.combo = 1 self.grade = 0 @@ -151,6 +151,7 @@ function MarathonA3Game:onPieceEnter() self:updateSectionTimes(self.level, self.level + 1) self.level = self.level + 1 self.speed_level = self.speed_level + 1 + self.torikan_passed = self.level >= 500 and true or false end end @@ -171,7 +172,7 @@ function MarathonA3Game:onLineClear(cleared_row_count) if not self.torikan_passed and self.level >= 500 and self.frames >= 25200 then self.level = 500 self.game_over = true - elseif self.level >= 500 then self.torikan_passed = true end + end end local cool_cutoffs = { diff --git a/tetris/modes/scoredrain.lua b/tetris/modes/scoredrain.lua new file mode 100644 index 0000000..d63aa8c --- /dev/null +++ b/tetris/modes/scoredrain.lua @@ -0,0 +1,186 @@ +require 'funcs' + +local GameMode = require 'tetris.modes.gamemode' +local Piece = require 'tetris.components.piece' + +local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag' + +local ScoreDrainGame = GameMode:extend() + +ScoreDrainGame.name = "Score Drain" +ScoreDrainGame.hash = "ScoreDrain" +ScoreDrainGame.tagline = "Your score goes down over time! Avoid hitting 0 points, or your game is over!" + +function ScoreDrainGame:new() + self.super:new() + + self.score = 2500 + self.drain_rate = 50 + self.combo = 1 + self.randomizer = History6RollsRandomizer() + + self.lock_drop = true + self.lock_hard_drop = true + self.enable_hold = true + self.next_queue_length = 3 +end + +function ScoreDrainGame:getARE() + if self.level < 700 then return 27 + elseif self.level < 800 then return 18 + elseif self.level < 1000 then return 14 + elseif self.level < 1100 then return 8 + elseif self.level < 1200 then return 7 + else return 6 end +end + +function ScoreDrainGame:getLineARE() + if self.level < 600 then return 27 + elseif self.level < 700 then return 18 + elseif self.level < 800 then return 14 + elseif self.level < 1100 then return 8 + elseif self.level < 1200 then return 7 + else return 6 end +end + +function ScoreDrainGame:getDasLimit() + if self.level < 500 then return 15 + elseif self.level < 900 then return 9 + else return 7 end +end + +function ScoreDrainGame:getLineClearDelay() + if self.level < 500 then return 40 + elseif self.level < 600 then return 25 + elseif self.level < 700 then return 16 + elseif self.level < 800 then return 12 + elseif self.level < 1100 then return 6 + elseif self.level < 1200 then return 5 + else return 4 end +end + +function ScoreDrainGame:getLockDelay() + if self.level < 900 then return 30 + elseif self.level < 1100 then return 17 + else return 15 end +end + +function ScoreDrainGame:getGravity() + if (self.level < 30) then return 4/256 + elseif (self.level < 35) then return 6/256 + elseif (self.level < 40) then return 8/256 + elseif (self.level < 50) then return 10/256 + elseif (self.level < 60) then return 12/256 + elseif (self.level < 70) then return 16/256 + elseif (self.level < 80) then return 32/256 + elseif (self.level < 90) then return 48/256 + elseif (self.level < 100) then return 64/256 + elseif (self.level < 120) then return 80/256 + elseif (self.level < 140) then return 96/256 + elseif (self.level < 160) then return 112/256 + elseif (self.level < 170) then return 128/256 + elseif (self.level < 200) then return 144/256 + elseif (self.level < 220) then return 4/256 + elseif (self.level < 230) then return 32/256 + elseif (self.level < 233) then return 64/256 + elseif (self.level < 236) then return 96/256 + elseif (self.level < 239) then return 128/256 + elseif (self.level < 243) then return 160/256 + elseif (self.level < 247) then return 192/256 + elseif (self.level < 251) then return 224/256 + elseif (self.level < 300) then return 1 + elseif (self.level < 330) then return 2 + elseif (self.level < 360) then return 3 + elseif (self.level < 400) then return 4 + elseif (self.level < 420) then return 5 + elseif (self.level < 450) then return 4 + elseif (self.level < 500) then return 3 + else return 20 + end +end + +function ScoreDrainGame:advanceOneFrame() + if self.ready_frames == 0 then + self.frames = self.frames + 1 + self.score = math.max(0, self.score - self.drain_rate / 60) + self.game_over = self.score <= 0 and true or false + end + return true +end + +function ScoreDrainGame:onPieceEnter() + if (self.level % 100 ~= 99) and self.frames ~= 0 then + self.level = self.level + 1 + end +end + +local cleared_row_levels = {1, 2, 4, 6} + +function ScoreDrainGame:onLineClear(cleared_row_count) + local new_level = self.level + cleared_row_levels[cleared_row_count] + self.drain_rate = math.floor(self.level / 100) < math.floor(new_level / 100) and self.drain_rate * 1.5 or self.drain_rate + self.level = new_level +end + +function ScoreDrainGame:updateScore(level, drop_bonus, cleared_lines) + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end + self.drop_bonus = 0 +end + +function ScoreDrainGame:drawGrid() + self.grid:draw() + if self.piece ~= nil and self.level < 100 then + self:drawGhostPiece(ruleset) + end +end + +function ScoreDrainGame:drawScoringInfo() + love.graphics.setColor(1, 1, 1, 1) + + love.graphics.setFont(font_3x5_2) + love.graphics.print( + self.das.direction .. " " .. + self.das.frames .. " " .. + strTrueValues(self.prev_inputs) + ) + love.graphics.printf("NEXT", 64, 40, 40, "left") + love.graphics.printf("DRAIN RATE", 240, 90, 80, "left") + love.graphics.printf("SCORE", 240, 170, 40, "left") + love.graphics.printf("TIME LEFT", 240, 250, 80, "left") + love.graphics.printf("LEVEL", 240, 320, 40, "left") + + love.graphics.setFont(font_3x5_3) + love.graphics.printf(math.floor(self.drain_rate).."/s", 240, 110, 120, "left") + love.graphics.printf(formatTime(self.score / self.drain_rate * 60), 240, 270, 120, "left") + love.graphics.printf(math.floor(self.score), 240, 190, 90, "left") + love.graphics.printf(self.level, 240, 340, 50, "right") + love.graphics.printf(self:getSectionEndLevel(), 240, 370, 50, "right") + + love.graphics.setFont(font_8x11) + love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") +end + +function ScoreDrainGame:getHighscoreData() + return { + level = self.level, + frames = self.frames, + } +end + +function ScoreDrainGame:getSectionEndLevel() + return math.floor(self.level / 100 + 1) * 100 +end + +function ScoreDrainGame:getBackground() + return math.floor(self.level / 100) +end + +return ScoreDrainGame \ No newline at end of file diff --git a/tetris/rulesets/crap.lua b/tetris/rulesets/crap.lua new file mode 100644 index 0000000..339c5db --- /dev/null +++ b/tetris/rulesets/crap.lua @@ -0,0 +1,173 @@ +local Piece = require 'tetris.components.piece' +local Ruleset = require 'tetris.rulesets.ruleset' + +local CRAP = Ruleset:extend() + +CRAP.name = "C.R.A.P." +CRAP.hash = "Completely Random Auto-Positioner" +CRAP.world = true +CRAP.colors={"C","O","M","R","G","Y","B"} +CRAP.colourscheme = { + I = CRAP.colors[math.ceil(math.random(7))], + L = CRAP.colors[math.ceil(math.random(7))], + J = CRAP.colors[math.ceil(math.random(7))], + S = CRAP.colors[math.ceil(math.random(7))], + Z = CRAP.colors[math.ceil(math.random(7))], + O = CRAP.colors[math.ceil(math.random(7))], + T = CRAP.colors[math.ceil(math.random(7))], +} +CRAP.softdrop_lock = true +CRAP.harddrop_lock = false + +CRAP.enable_IRS_wallkicks = true + +CRAP.spawn_positions = { + I = { x=5, y=4 }, + 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 }, +} + +CRAP.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 }, +} + +CRAP.block_offsets = { + I={ + { {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} }, + { {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} }, + { {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} }, + }, + 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=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=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} }, + } +} + +-- Component functions. + +function CRAP:attemptRotate(new_inputs, piece, grid, initial) + local rot_dir = 0 + + if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then + rot_dir = 3 + elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then + rot_dir = 1 + elseif (new_inputs["rotate_180"]) then + rot_dir = self:get180RotationValue() + end + + if rot_dir == 0 then return end + if self.world and config.gamesettings.world_reverse == 2 then + rot_dir = 4 - rot_dir + end + + local new_piece = piece:withRelativeRotation(rot_dir) + + self:attemptWallkicks(piece, new_piece, rot_dir, grid) +end + +function CRAP:attemptWallkicks(piece, new_piece, rot_dir, grid) + + for i=1,20 do + dx=math.floor(math.random(11))-5 + dy=math.floor(math.random(11))-5 + if grid:canPlacePiece(new_piece:withOffset({x=dx, y=dy})) then + piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=dy}) + self:onPieceRotate(piece, grid) + return + end + end + +end + +function CRAP:onPieceCreate(piece, grid) + CRAP:randomizeColours() + piece.manipulations = 0 + piece.rotations = 0 +end + +function CRAP:onPieceDrop(piece, grid) + CRAP:randomizeColours() + piece.lock_delay = 0 -- step reset +end + +function CRAP:onPieceMove(piece, grid) + CRAP:randomizeColours() + piece.lock_delay = 0 -- move reset + if piece:isDropBlocked(grid) then + piece.manipulations = piece.manipulations + 1 + if piece.manipulations >= 10 then + piece.locked = true + end + end +end + +function CRAP:onPieceRotate(piece, grid) + CRAP:randomizeColours() + piece.lock_delay = 0 -- rotate reset + if piece:isDropBlocked(grid) then + piece.rotations = piece.rotations + 1 + if piece.rotations >= 8 then + piece.locked = true + end + end +end + +function CRAP:get180RotationValue() return 2 end + +function CRAP:randomizeColours() + CRAP.colourscheme = { + I = CRAP.colors[math.ceil(math.random(7))], + L = CRAP.colors[math.ceil(math.random(7))], + J = CRAP.colors[math.ceil(math.random(7))], + S = CRAP.colors[math.ceil(math.random(7))], + Z = CRAP.colors[math.ceil(math.random(7))], + O = CRAP.colors[math.ceil(math.random(7))], + T = CRAP.colors[math.ceil(math.random(7))], + } +end + +return CRAP diff --git a/tetris/rulesets/dtet.lua b/tetris/rulesets/dtet.lua new file mode 100644 index 0000000..1560bd0 --- /dev/null +++ b/tetris/rulesets/dtet.lua @@ -0,0 +1,153 @@ +local Piece = require 'tetris.components.piece' +local Ruleset = require 'tetris.rulesets.ruleset' + +local DTET = Ruleset:extend() + +DTET.name = "D.R.S." +DTET.hash = "DTET" + +DTET.spawn_positions = { + I = { x=5, y=4 }, + 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 }, +} + +DTET.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 }, +} + +DTET.block_offsets = { + I={ + { {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} }, + { {x=-1, y=-1}, {x=-1, y=-2}, {x=-1, y=0}, {x=-1, 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=-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} }, + { {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} }, + }, + L={ + { {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} }, + { {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} }, + }, + 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=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} }, + { {x=1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=-2} }, + }, + T={ + { {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} }, + { {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} }, + }, + Z={ + { {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} }, + { {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2} }, + { {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} }, + { {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0} }, + } +} + +-- clockwise kicks: {{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, +-- counterclockwise kicks: {{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + +DTET.wallkicks_3x3 = { + [0]={ + [1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, + [1]={ + [0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [2]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, + [2]={ + [0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [3]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + }, + [3]={ + [0]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, +}; + +DTET.wallkicks_line = { + [0]={ + [1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, + [1]={ + [0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [2]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, + [2]={ + [0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, + [3]={ + [0]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, + [1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + [2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}}, + }, +}; + +function DTET:attemptWallkicks(piece, new_piece, rot_dir, grid) + + local kicks + if piece.shape == "O" then + return + elseif piece.shape == "I" then + kicks = DTET.wallkicks_line[piece.rotation][new_piece.rotation] + else + kicks = DTET.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 DTET:onPieceDrop(piece, grid) + piece.lock_delay = 0 -- step reset +end + +function DTET:getDefaultOrientation() return 1 end + +return DTET \ No newline at end of file diff --git a/tetris/rulesets/eheart.lua b/tetris/rulesets/eheart.lua new file mode 100644 index 0000000..ddadab0 --- /dev/null +++ b/tetris/rulesets/eheart.lua @@ -0,0 +1,45 @@ +local Piece = require 'tetris.components.piece' +local ARS = require 'tetris.rulesets.arika' + +local EHeart = ARS:extend() + +EHeart.name = "E-Heart ARS" +EHeart.hash = "EHeartARS" + +function EHeart:attemptWallkicks(piece, new_piece, rot_dir, grid) + + -- I and O don't kick + if (piece.shape == "I" or piece.shape == "O") then return end + + -- center column rule (kicks) + local offsets = new_piece:getBlockOffsets() + table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end) + for index, offset in pairs(offsets) do + if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then + -- individual checks for all 9 cells, in the given order + if offset.y < 0 then + if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid) + elseif offset.x == 0 then return + elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end + elseif offset.y == 0 then + if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid) + elseif offset.x == 0 then return + elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end + elseif offset.y > 0 then + if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid) + elseif offset.x == 0 then return + elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end + end + end + end + +end + +function EHeart:lateralKick(dx, piece, new_piece, rot_dir, grid) + if (grid:canPlacePiece(new_piece:withOffset({x=dx, y=0}))) then + piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=0}) + self:onPieceRotate(piece, grid) + end +end + +return EHeart diff --git a/tetris/rulesets/pptprs.lua b/tetris/rulesets/pptprs.lua new file mode 100644 index 0000000..9e55a82 --- /dev/null +++ b/tetris/rulesets/pptprs.lua @@ -0,0 +1,102 @@ +local Piece = require 'tetris.components.piece' +local SRS = require 'tetris.rulesets.standard_exp' + +local PPTPRS = SRS:extend() + +PPTPRS.name = "PPTPRS" +PPTPRS.hash = "Puyo Tetris Pentos" + +PPTPRS.block_offsets = { + I={ + { {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0}, {x=-3, y=0} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2}, {x=0, y=-2} }, + { {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1}, {x=2, y=1} }, + { {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2}, {x=-1, y=3} }, + }, + J={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=0, y=1} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1}, {x=-1, y=0} }, + { {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1}, {x=0, y=-1} }, + { {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1}, {x=1, y=0} }, + }, + L={ + { {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1}, {x=-1, y=1} }, + { {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=-1} }, + { {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1}, {x=1, y=-1} }, + { {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=1} }, + }, + O={ + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=-1, y=1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=-2, y=-1} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2} }, + { {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=0} }, + }, + S={ + { {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0} }, + { {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2} }, + { {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} }, + { {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} }, + }, + T={ + { {x=0, y=0}, {x=-1, 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=1}, {x=1, y=0}, {x=2, y=0} }, + { {x=0, y=0}, {x=1, 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=-1}, {x=-1, y=0}, {x=-2, 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=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1}, {x=-1, 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=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} }, + } +} + +PPTPRS.wallkicks_O = { + [0]={ + [1]={{x=0, y=1}}, + [2]={{x=0, y=1}}, + [3]={{x=-1, y=0}}, + }, + [1]={ + [0]={{x=0, y=-1}}, + [2]={{x=-1, y=0}}, + [3]={{x=-1, y=0}}, + }, + [2]={ + [0]={{x=0, y=-1}}, + [1]={{x=1, y=0}}, + [3]={{x=0, y=-1}}, + }, + [3]={ + [0]={{x=1, y=0}}, + [1]={{x=1, y=0}}, + [2]={{x=0, y=1}}, + }, +} + +function PPTPRS:attemptWallkicks(piece, new_piece, rot_dir, grid) + local kicks + + if piece.shape == "O" then + kicks = PPTPRS.wallkicks_O[piece.rotation][new_piece.rotation] + elseif piece.shape == "I" then + kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation] + else + kicks = SRS.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 + +return PPTPRS