Whole bunch of good stuff added

This commit is contained in:
Ishaan Bhardwaj 2020-12-04 20:53:09 -05:00
parent 3776393354
commit 3674c78609
26 changed files with 1347 additions and 218 deletions

View File

@ -0,0 +1,175 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local BeginnerA2Game = GameMode:extend()
BeginnerA2Game.name = "Beginner A2"
BeginnerA2Game.hash = "BeginnerA2"
BeginnerA2Game.tagline = "How many points can you score in 300 levels?"
function BeginnerA2Game:new()
self.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.piece_time = 0
self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM"
}
self.lock_drop = false
self.lock_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
end
function BeginnerA2Game:getARE() return 25 end
function BeginnerA2Game:getLineARE() return self:getARE() end
function BeginnerA2Game:getLineClearDelay() return 40 end
function BeginnerA2Game:getDasLimit() return 14 end
function BeginnerA2Game:getLockDelay() return 30 end
function BeginnerA2Game:getGravity()
if self.level < 8 then return 4/256
elseif self.level < 19 then return 5/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 < 108 then return 4/256
elseif self.level < 119 then return 5/256
elseif self.level < 125 then return 6/256
elseif self.level < 131 then return 8/256
elseif self.level < 139 then return 12/256
elseif self.level < 149 then return 32/256
elseif self.level < 156 then return 48/256
elseif self.level < 164 then return 80/256
elseif self.level < 174 then return 112/256
elseif self.level < 180 then return 128/256
elseif self.level < 200 then return 144/256
elseif self.level < 212 then return 16/256
elseif self.level < 221 then return 48/256
elseif self.level < 232 then return 80/256
elseif self.level < 244 then return 112/256
elseif self.level < 256 then return 144/256
elseif self.level < 267 then return 176/256
elseif self.level < 277 then return 192/256
elseif self.level < 287 then return 208/256
elseif self.level < 295 then return 224/256
elseif self.level < 300 then return 240/256
else return 20 end
end
function BeginnerA2Game:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 1800 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function BeginnerA2Game:onPieceEnter()
if self.level ~= 299 and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function BeginnerA2Game:updateScore(level, drop_bonus, cleared_lines)
if not self.clear then
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
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 * self.bravo +
math.ceil(math.min(300, level + cleared_lines) / 2) +
math.max(self:getLockDelay() - self.piece_time, 0) * 7)
* 6
)
else
self.combo = 1
end
self.drop_bonus = 0
self.piece_time = 0
end
end
function BeginnerA2Game:whilePieceActive()
self.piece_time = self.piece_time + 1
end
function BeginnerA2Game:onLineClear(cleared_row_count)
self.level = math.min(self.level + cleared_row_count, 300)
if self.level == 300 and not self.clear then
self.clear = true
self.score = self.score + (
1253 * math.ceil(math.max(0, 18000 - self.frames) / 60)
)
end
end
function BeginnerA2Game:drawGrid()
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
function BeginnerA2Game:getHighscoreData()
return {
score = self.score,
level = self.level,
frames = self.frames,
}
end
function BeginnerA2Game:getBackground()
return math.floor(self.level / 100)
end
function BeginnerA2Game:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
self.piece_time .. " " ..
strTrueValues(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
local sg = self.grid:checkSecretGrade()
if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(300, 240, 370, 40, "right")
if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
return BeginnerA2Game

40
tetris/modes/big_meme.lua Normal file
View File

@ -0,0 +1,40 @@
require 'funcs'
local SurvivalA3Game = require 'tetris.modes.survival_a3'
local BigMemeGame = SurvivalA3Game:extend()
BigMemeGame.name = "Big Survival A3"
BigMemeGame.hash = "BigA3"
BigMemeGame.tagline = "The blocks are bigger and the speeds are faster!"
function BigMemeGame:initialize(ruleset)
self.super:initialize(ruleset)
self.big_mode = true
end
local cleared_row_levels = {1, 2, 4, 6}
function BigMemeGame:onLineClear(cleared_row_count)
cleared_row_count = cleared_row_count / 2
if not self.clear then
local new_level = self.level + cleared_row_levels[cleared_row_count]
self:updateSectionTimes(self.level, new_level)
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
self.clear = true
if new_level >= 1300 then
self.level = 1300
self.grid:clear()
self.big_mode = true
self.roll_frames = -150
else
self.game_over = true
end
else
self.level = math.min(new_level, 1300)
end
self:advanceBottomRow(-cleared_row_count)
end
end
return BigMemeGame

119
tetris/modes/credits_a3.lua Normal file
View File

@ -0,0 +1,119 @@
require 'funcs'
local IntervalTrainingMode = require 'tetris.modes.interval_training'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local CreditsA3Game = IntervalTrainingMode:extend()
CreditsA3Game.name = "Credits A3"
CreditsA3Game.hash = "CreditsA3"
CreditsA3Game.tagline = "How consistently can you clear the Ti M-roll?"
function CreditsA3Game:new()
CreditsA3Game.super:new()
self.section_time_limit = 3238
self.norm = 0
self.section = 0
end
function CreditsA3Game:advanceOneFrame(inputs, ruleset)
if self.frames == 0 then
switchBGM("credit_roll", "gm3")
end
if self.roll_frames > 0 then
self.roll_frames = self.roll_frames - 1
if self.roll_frames == 0 then
-- reset
self.norm = 0
self.frames = 0
self.grid:clear()
switchBGM("credit_roll", "gm3")
self:initializeOrHold(inputs, ruleset)
else
return false
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
if self:getSectionTime() >= self.section_time_limit then
self.norm = self.norm + 16
self.piece = nil
if self.norm >= 60 then
self.section = self.section + 1
self.roll_frames = 150
else
self.game_over = true
switchBGM(nil)
end
end
end
return true
end
function CreditsA3Game:onPieceEnter()
-- do nothing
end
function CreditsA3Game:onLineClear(cleared_row_count)
if not self.clear then
self.norm = self.norm + (cleared_row_count == 4 and 10 or cleared_row_count)
end
end
CreditsA3Game.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function CreditsA3Game:drawGrid(ruleset)
if not self.game_over and self.roll_frames < 30 then
self.grid:drawInvisible(self.rollOpacityFunction)
else
self.grid:draw()
end
end
function CreditsA3Game: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("TIME LEFT", 240, 250, 80, "left")
love.graphics.printf("NORM", 240, 320, 40, "left")
self:drawSectionTimesWithSplits(self.section)
love.graphics.setFont(font_3x5_3)
-- draw time left, flash red if necessary
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
if self.norm >= 44 then
love.graphics.setColor(0.3, 1, 0.3, 1) -- flash green if goal has been cleared
else
love.graphics.setColor(1, 0.3, 0.3, 1) -- otherwise flash red
end
end
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(self.norm, 240, 340, 40, "right")
if self.game_over or self.roll_frames > 0 then
love.graphics.printf("60", 240, 370, 40, "right")
else
love.graphics.printf("44", 240, 370, 40, "right")
end
end
function CreditsA3Game:getBackground()
return self.section
end
return CreditsA3Game

View File

@ -206,7 +206,9 @@ function GLock:drawScoringInfo()
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
if self:getGSection() == 11 then if self:getGSection() == 11 then
if self.frames <= 18000 then love.graphics.printf("GM", 240, 140, 90, "left") -- blame gizmo4487 for this time
-- he's a legend
if self.frames <= frameTime(5,19) then love.graphics.printf("GM", 240, 140, 90, "left")
else love.graphics.printf("M", 240, 140, 90, "left") end else love.graphics.printf("M", 240, 140, 90, "left") end
else else
love.graphics.printf("G"..1+self:getGSection(), 240, 140, 90, "left") love.graphics.printf("G"..1+self:getGSection(), 240, 140, 90, "left")

131
tetris/modes/kamui.lua Normal file
View File

@ -0,0 +1,131 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Randomizer = require 'tetris.randomizers.kamui_sequence'
local KamuiGame = GameMode:extend()
KamuiGame.name = "Race A3"
KamuiGame.hash = "RaceA3"
KamuiGame.tagline = "How fast can you reach level 500?"
function KamuiGame:new()
self.super:new()
self.randomizer = Randomizer()
self.time_limit = 18000
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function KamuiGame:getARE()
if self.level < 100 then return 16
elseif self.level < 300 then return 12
elseif self.level < 400 then return 6
else return 5 end
end
function KamuiGame:getLineARE()
if self.level < 100 then return 12
elseif self.level < 400 then return 6
else return 5 end
end
function KamuiGame:getLineClearDelay() return self:getLineARE() end
function KamuiGame:getDasLimit()
if self.level < 200 then return 10
elseif self.level < 300 then return 9
elseif self.level < 400 then return 8
else return 6 end
end
function KamuiGame:getLockDelay()
if self.level < 100 then return 30
elseif self.level < 200 then return 26
elseif self.level < 300 then return 22
elseif self.level < 400 then return 18
else return 15 end
end
function KamuiGame:getGravity() return 20 end
function KamuiGame:onPieceEnter()
if self.level % 100 ~= 99 and self.frames ~= 0 then
self.level = self.level + 1
end
end
function KamuiGame:onLineClear(cleared_row_count)
local cleared_row_levels = {1, 2, 4, 6}
local new_level = self.level + cleared_row_levels[cleared_row_count]
self:updateSectionTimes(self.level, new_level)
self.level = math.min(500, new_level)
self.game_over = self.level == 500
end
function KamuiGame:updateSectionTimes(old_level, new_level)
if math.floor(old_level / 100) < math.floor(new_level / 100) then
-- record new section
table.insert(self.section_times, self.frames - self.section_start_time)
self.section_start_time = self.frames
end
end
function KamuiGame:advanceOneFrame()
if self.ready_frames == 0 then
self.frames = self.frames + 1
self.time_limit = self.time_limit - 1
end
return true
end
function KamuiGame:drawGrid() self.grid:draw() end
function KamuiGame: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("TIME LEFT", 240, 250, 80, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self.level == 500 and self.level or (math.floor(self.level / 100) + 1) * 100, 240, 370, 40, "right")
-- draw time left, flash red if necessary
if not self.game_over and self.time_limit < frameTime(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(self.time_limit), 240, 270, 160, "left")
love.graphics.setColor(1, 1, 1, 1)
end
function KamuiGame:getBackground()
return math.floor(self.level / 100)
end
function KamuiGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return KamuiGame

View File

@ -27,6 +27,7 @@ function MarathonC88Game:new()
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
self.irs = false
self.grid:applyCeiling(4) self.grid:applyCeiling(4)
end end

View File

@ -0,0 +1,135 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local MarathonWCBGame = GameMode:extend()
MarathonWCBGame.name = "Marathon WCB"
MarathonWCBGame.hash = "MarathonWCB"
MarathonWCBGame.tagline = "When all the pieces slip right to their destinations... can you keep up?"
function MarathonWCBGame:new()
MarathonWCBGame.super:new()
self.pieces = 0
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.lock_hard_drop = true
self.instant_hard_drop = true
self.instant_soft_drop = true
self.enable_hold = false
self.next_queue_length = 3
self.piece_is_active = false
end
function MarathonWCBGame:getDropSpeed()
return 20
end
function MarathonWCBGame:getARR()
return 0
end
function MarathonWCBGame:getARE()
return 0
end
function MarathonWCBGame:getLineARE()
return 0
end
function MarathonWCBGame:getDasLimit()
return 0
end
function MarathonWCBGame:getLineClearDelay()
return 0
end
function MarathonWCBGame:getLockDelay()
return math.huge
end
function MarathonWCBGame:getGravity()
return self.piece_is_active and 20 or 0
end
function MarathonWCBGame:advanceOneFrame()
if self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function MarathonWCBGame:onAttemptPieceMove()
if self.piece ~= nil then
-- don't let the piece move before it's finished dropping
self.piece:dropToBottom(self.grid)
end
self.piece_is_active = true
end
function MarathonWCBGame:onAttemptPieceRotate()
self.piece_is_active = true
end
function MarathonWCBGame:onPieceLock()
self.super:onPieceLock()
self.piece_is_active = false
self.pieces = self.pieces + 1
end
function MarathonWCBGame:onLineClear(cleared_row_count)
self.lines = self.lines + cleared_row_count
end
function MarathonWCBGame:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function MarathonWCBGame:getHighscoreData()
return {
pieces = self.pieces,
frames = self.frames,
}
end
function MarathonWCBGame:drawScoringInfo()
MarathonWCBGame.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("lines", text_x, 160, 80, "left")
love.graphics.printf("pieces", text_x, 220, 80, "left")
love.graphics.printf("piece/sec", text_x, 280, 80, "left")
local sg = self.grid:checkSecretGrade()
if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.lines, text_x, 180, 80, "left")
love.graphics.printf(self.pieces, text_x, 240, 80, "left")
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 300, 80, "left")
if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end
end
function MarathonWCBGame:getBackground()
return (math.floor(self.pieces / 50) % 20)
end
return MarathonWCBGame

77
tetris/modes/non.lua Normal file
View File

@ -0,0 +1,77 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart'
local NightOfNights = GameMode:extend()
NightOfNights.name = "Night of Nights"
NightOfNights.hash = "NightOfNights"
NightOfNights.tagline = "The pieces lock down super fast! How long can you survive?"
function NightOfNights:new()
self.super:new()
self.active_time = 0
self.randomizer = Bag7NoSZOStartRandomizer()
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function NightOfNights:getARE() return 0 end
function NightOfNights:getLineARE() return 0 end
function NightOfNights:getLineClearDelay() return 0 end
function NightOfNights:getDasLimit() return 6 end
function NightOfNights:getGravity() return 20 end
function NightOfNights:whilePieceActive()
self.active_time = self.active_time + 1
if self.active_time >= 20 then self.piece.locked = true end
end
function NightOfNights:onPieceLock(piece, cleared_row_count)
self.super:onPieceLock()
self.active_time = 0
self.lines = self.lines + cleared_row_count
end
function NightOfNights:drawGrid()
self.grid:draw()
end
function NightOfNights: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("LINES", 240, 200, 40, "left")
love.graphics.setFont(font_3x5_4)
love.graphics.printf(self.lines, 240, 220, 90, "left")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function NightOfNights:getBackground()
return 19
end
function NightOfNights:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return NightOfNights

136
tetris/modes/pro_tl.lua Normal file
View File

@ -0,0 +1,136 @@
require 'funcs'
local MarathonAX4Game = require 'tetris.modes.marathon_ax4'
local TetraRandomizer = require 'tetris.randomizers.tetra'
local ProGame = MarathonAX4Game:extend()
ProGame.name = "Final TL"
ProGame.hash = "ProTL"
ProGame.tagline = "Your next pieces start disappearing! What lies past Mach 9?"
function ProGame:new()
self.super:new()
self.next_queue_length = 6
self.randomizer = TetraRandomizer()
end
function ProGame:getARE() return 6 end
function ProGame:getLineARE() return 6 end
function ProGame:getLineClearDelay() return 6 end
function ProGame:getDasLimit() return 6 end
function ProGame:getDropSpeed() return 20 end
function ProGame:getGravity()
if self.lines < 20 then return 1
elseif self.lines < 40 then return 3
elseif self.lines < 60 then return 5
else return 20 end
end
function ProGame:getLockDelay()
if self.lines < 20 then return 30
elseif self.lines < 40 then return 29
elseif self.lines < 60 then return 27
elseif self.lines < 80 then return 23
elseif self.lines < 100 then return 21
elseif self.lines < 120 then return 20
elseif self.lines < 140 then return 18
elseif self.lines < 160 then return 17
elseif self.lines < 180 then return 15
else return 14 end
end
function ProGame:advanceOneFrame(inputs, ruleset)
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames > 2968 then
self.completed = true
end
elseif self.ready_frames == 0 then
if not self.section_clear then
self.frames = self.frames + 1
end
if self:getSectionTime() >= self.section_time_limit then
self.game_over = true
end
end
ruleset.onPieceMove = function() end
ruleset.onPieceRotate = function() end
return true
end
function ProGame:onLineClear(cleared_row_count)
if not self.clear then
local new_lines = self.lines + cleared_row_count
self:updateSectionTimes(self.lines, new_lines)
self.lines = math.min(new_lines, 200)
if self.lines == 200 then
self.grid:clear()
self.clear = true
self.roll_frames = -150
else
self.enable_hold = self.lines < 160
self.next_queue_length = math.max(1, 6 - math.floor(self.lines / 20))
end
end
end
function ProGame:updateSectionTimes(old_lines, new_lines)
if math.floor(old_lines / 20) < math.floor(new_lines / 20) then
-- record new section
table.insert(self.section_times, self:getSectionTime())
self.section_start_time = self.frames
self.section_clear = true
end
end
function ProGame:drawGrid()
self.grid:draw()
self:drawGhostPiece()
end
function ProGame:drawScoringInfo()
MarathonAX4Game.super.drawScoringInfo(self)
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")
if self.lines < 200 then love.graphics.printf("TIME LEFT", 240, 250, 80, "left") end
love.graphics.printf("LINES", 240, 320, 40, "left")
local current_section = math.floor(self.lines / 20) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.lines, 240, 340, 40, "right")
love.graphics.printf(self.clear and self.lines or self:getSectionEndLines(), 240, 370, 40, "right")
-- draw time left, flash red if necessary
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
if self.lines < 200 then love.graphics.printf(formatTime(time_left), 240, 270, 160, "left") end
love.graphics.setColor(1, 1, 1, 1)
end
function ProGame:getSectionEndLines()
return math.floor(self.lines / 20 + 1) * 20
end
function ProGame:getBackground()
return math.floor(self.lines / 20)
end
return ProGame

View File

@ -58,7 +58,7 @@ function Race40Game:getLineARE()
end end
function Race40Game:getDasLimit() function Race40Game:getDasLimit()
return 10 return 8
end end
function Race40Game:getLineClearDelay() function Race40Game:getLineClearDelay()

135
tetris/modes/speed.lua Normal file
View File

@ -0,0 +1,135 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Bag7Randomizer = require 'tetris.randomizers.bag7'
local LudicrousSpeed = GameMode:extend()
LudicrousSpeed.name = "Ludicrous Speed"
LudicrousSpeed.hash = "LSpeed"
LudicrousSpeed.tagline = "Don't make Keanu sad. Always stay above the speed limit."
function LudicrousSpeed:new()
self.super:new()
self.time_limit = 301
self.pps = {}
self.pps_limit = 1
self.pieces = 0
self.randomizer = Bag7Randomizer()
self.lock_drop = true
self.lock_hard_drop = true
self.instant_soft_drop = false
self.instant_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
self.irs = false
self.ihs = false
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
else return 20 end
end
function LudicrousSpeed:getARE() return 0 end
function LudicrousSpeed:getLineARE() return 0 end
function LudicrousSpeed:getLineClearDelay() return 0 end
function LudicrousSpeed:getDasLimit() return 8 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)
end
function LudicrousSpeed:advanceOneFrame()
if self.ready_frames == 0 then
self.frames = self.frames + 1
if mean(self.pps) * 2 < self.pps_limit then
self.time_limit = self.time_limit - 1
self.game_over = self.time_limit <= 0
else self.time_limit = 301 end
if self.frames % 30 == 0 and self.frames ~= 0 then
if table.getn(self.pps) == 30 then
table.remove(self.pps, 1)
table.insert(self.pps, self.pieces)
else table.insert(self.pps, self.pieces) end
self.pieces = 0
--print(table.concat(self.pps, " "))
end
end
return true
end
function LudicrousSpeed:onPieceLock()
self.super:onPieceLock()
self.pieces = self.pieces + 1
end
function LudicrousSpeed:onLineClear(cleared_row_count)
self.lines = self.lines + cleared_row_count
self.pps_limit = 1 + math.floor(self.lines / 10) / 10
end
function LudicrousSpeed:drawGrid()
self.grid:draw()
self:drawGhostPiece()
end
function LudicrousSpeed:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("NEXT", 64, 40, 40, "left")
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")
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
strTrueValues(self.prev_inputs) ..
self.drop_bonus
)
love.graphics.setFont(font_3x5_4)
if self.time_limit <= 300 then love.graphics.printf(formatTime(self.time_limit), 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 > 30 and mean(self.pps) * 2 or 0), text_x, 200, 80, "left")
love.graphics.printf(string.format("%.02f", self.pps_limit), text_x, 260, 80, "left")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function LudicrousSpeed:getBackground()
return math.floor(self.lines / 10)
end
function LudicrousSpeed:getHighscoreData()
return {
lines = self.lines,
frames = self.frames,
}
end
return LudicrousSpeed

View File

@ -0,0 +1,15 @@
require 'funcs'
local MarathonC88Game = require 'tetris.modes.marathon_c88'
local SurvivalC88Game = MarathonC88Game:extend()
SurvivalC88Game.name = "Survival C88"
SurvivalC88Game.hash = "Shimizu"
SurvivalC88Game.tagline = "You can't rotate the pieces initially! What will you do?"
function SurvivalC88Game:getGravity()
return 20
end
return SurvivalC88Game

View File

@ -0,0 +1,16 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Sequence = Randomizer:extend()
function Sequence:initialize()
self.sequence = "IJLOT"
self.counter = 0
end
function Sequence:generatePiece()
local piece = string.sub(self.sequence, self.counter + 1, self.counter + 1)
self.counter = (self.counter + 1) % string.len(self.sequence)
return piece
end
return Sequence

View File

@ -0,0 +1,10 @@
local Sequence = require 'tetris.randomizers.fixed_sequence'
local KamuiSequence = Sequence:extend()
function KamuiSequence:initialize()
self.super:initialize()
self.sequence = "JISTZOSJITOZLISTJLOSZTJOILZJOITZJLISZTLJIOZLSTJZLSOTJLIZSTJOLISTZJOLTZSOJLZIOJSLZTJSIZOJTLSIJTLZSOJTZSLIOZSJLTZOISTZLOJTZILOSTIJOLSTIZLJOTZILSJZTOSIZJOLTISJZOLITSJZLTSOJLTZOJISTZLOSIZJTSIOZLSJIZTLJIZTLSJZILOTJZLOISZJLOTSJIZTOLSZJITSOZITSLJOTSLJOZITJOZLSTIOJSTLOIJTSOZILTSOJZILSOTZLSJTIZSJOTILZJOISLJZITLOZJTILSJTIZOJTIZOLJTSOZJTSILJZTSOIZJSTOIJLZTIJOLSISOJTLZOIJLTZOILSSJTOZILSOTZLSJIOLZJTSOIJZTSOJLISTZLIOJTZSSOLIZSTJOSLTIZSJLOTZISLJOTZLJOSTZJIOLSTIZOSLJZISOJLIZTOJILZOJILTOJISZOTJTIOSZTLJSZOITLZOJSILZJOILJZOISJTLOZSJIOLJSZTLISJOZLISOJZILOSTILOTJSLZITSOJITLZSJTIZOLJISTOJILZSTOILZSJOTISLOZJITOLZJSILOZSIJLZTSOJZLTIJSLTIZJLOSTJZLSIOTJZLOSOTSLIOZSTJOILSZTJLOZSTILZSOJITLZSIJTOLZSTJOZSLJIZSOJLZITJSLZOJTLSOIZJSOSLTZOSSTLZOIJSTOZILJSOSTIJLSOTJLSOTIJSZOLTSJZLOSJTZILOTSZLOITZSJITLZJIOSZTLIJOSLIZTOLIZTJLSZOTJLSOTZJIOSZTJTOSZIJTSOIZJLSOSILJTZOIJSTIJLTOSZJLOTSIZOLSTJIOJZTILSOJZTILJZSOSITLZSOTJIZOSLJTOSZLITSJLIZTOLSIZOJTSZOJTLZSJTOILZJTSLZJTISLZTJISOLZTSIOLJTZIOJSLTIJZSTLJIOTSLZJITLOJIZLZSOIJLZOTSSJZTIO"
end
return KamuiSequence

View File

@ -0,0 +1,72 @@
-- A randomizer based on Tetra Legends' "Tetra-X" game mode
-- There are two main rules:
-- * if piece hasn't been generated for 13+ pieces, force it
-- * if piece has been generated in the last two pieces, don't give it out
--
-- The official implementation also has self-balancing functionality (bias
-- towards pieces that have appeared less often).
local Randomizer = require 'tetris.randomizers.randomizer'
local TetraXRandomizer = Randomizer:extend()
function TetraXRandomizer:initialize()
local pieces = {"I", "J", "L", "O", "S", "T", "Z"}
self.count = {}
self.lastseen = {}
self.totalcount = 0
self.pieceselection = pieces
for _, piece in ipairs(pieces) do
self.count[piece] = 0
self.lastseen[piece] = -1 -- use -1 as magic value for "not seen"
end
end
function TetraXRandomizer:generatePiece()
local generated = nil
while true do
generated = self.pieceselection[math.random(#self.pieceselection)]
if not (self.lastseen[generated] == 0 or self.lastseen[generated] == 1) then
break
end
end
-- piece has not been generated in the last 13+
for piece, val in pairs(self.lastseen) do
if val >= 13 then
generated = piece
break
end
end
for piece, val in pairs(self.lastseen) do
if piece == generated then
self.lastseen[piece] = 0
else
if val ~= -1 then
self.lastseen[piece] = val + 1
end
end
end
self.count[generated] = self.count[generated] + 1
self.totalcount = self.totalcount + 1
-- shuffle the piece selection for the next time
self.pieceselection = {}
for piece, count in pairs(self.count) do
-- in this case 6 = #piece types - 1, so all probabilities sum to 1
local probability = (self.totalcount - count) / (self.totalcount * 6)
local chances = math.floor(probability * 1000 + 0.5) -- simulated "round"
for _ = 1, chances do
table.insert(self.pieceselection, piece)
end
end
return generated
end
return TetraXRandomizer

147
tetris/rulesets/bonkers.lua Normal file
View File

@ -0,0 +1,147 @@
local Ruleset = require "tetris.rulesets.ruleset"
local BONKERS = Ruleset:extend()
BONKERS.name = "B.O.N.K.E.R.S."
BONKERS.hash = "Bonkers"
BONKERS.world = true
BONKERS.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 },
}
BONKERS.colourscheme = {
I = "G",
J = "G",
L = "G",
O = "G",
S = "G",
T = "G",
Z = "G",
}
BONKERS.softdrop_lock = false
BONKERS.harddrop_lock = true
BONKERS.enable_IRS_wallkicks = true
BONKERS.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 BONKERS: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 config.gamesettings.world_reverse == 3 or (self.world and config.gamesettings.world_reverse == 2) then
rot_dir = 4 - rot_dir
end
local new_piece = piece:withRelativeRotation(rot_dir)
if (grid:canPlacePiece(new_piece)) then
if piece:isDropBlocked(grid) then
self:attemptWallkicks(piece, new_piece, rot_dir, grid)
else
piece:setRelativeRotation(rot_dir)
self:onPieceRotate(piece, grid)
end
else
if not(initial and self.enable_IRS_wallkicks == false) then
self:attemptWallkicks(piece, new_piece, rot_dir, grid)
end
end
end
function BONKERS:attemptWallkicks(piece, new_piece, rot_dir, grid)
if piece.shape == "I" then
horizontal_kicks = {0, 1, -1, 2, -2}
else
horizontal_kicks = {0, 1, -1}
end
for y_offset = 24, -24, -1 do
for idx, x_offset in pairs(horizontal_kicks) do
local offset = {x=x_offset, y=y_offset}
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
piece.lock_delay = 0 -- rotate reset
return
end
end
end
end
function BONKERS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset
end
function BONKERS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
end
function BONKERS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset
end
return BONKERS

View File

@ -115,8 +115,8 @@ function CRAP:attemptWallkicks(piece, new_piece, rot_dir, grid)
dx=math.floor(math.random(11))-5 dx=math.floor(math.random(11))-5
dy=math.floor(math.random(11))-5 dy=math.floor(math.random(11))-5
if grid:canPlacePiece(new_piece:withOffset({x=dx, y=dy})) then if grid:canPlacePiece(new_piece:withOffset({x=dx, y=dy})) then
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=dy})
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=dy})
return return
end end
end end

View File

@ -27,9 +27,9 @@ function Cultris:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end

View File

@ -32,22 +32,22 @@ DTET.big_spawn_positions = {
DTET.block_offsets = { DTET.block_offsets = {
I={ 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=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=-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={ 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=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=-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={ 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=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=-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={ 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} },
@ -62,10 +62,10 @@ DTET.block_offsets = {
{ {x=1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=-2} }, { {x=1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=-2} },
}, },
T={ 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=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=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={ Z={
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} }, { {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
@ -75,64 +75,18 @@ DTET.block_offsets = {
} }
} }
-- clockwise kicks: {{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}}, DTET.wallkicks_cw = {{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_ccw = {{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) function DTET:attemptWallkicks(piece, new_piece, rot_dir, grid)
local kicks local kicks
if piece.shape == "O" then if piece.shape == "O" then
return return
elseif piece.shape == "I" then elseif rot_dir == 1 then
kicks = DTET.wallkicks_line[piece.rotation][new_piece.rotation] kicks = DTET.wallkicks_cw
else else
kicks = DTET.wallkicks_3x3[piece.rotation][new_piece.rotation] kicks = DTET.wallkicks_ccw
end end
assert(piece.rotation ~= new_piece.rotation) assert(piece.rotation ~= new_piece.rotation)
@ -140,18 +94,19 @@ function DTET:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end
end end
function DTET:onPieceDrop(piece, grid) function DTET:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function DTET:getDefaultOrientation() return 1 end function DTET:getDefaultOrientation() return 3 end
return DTET return DTET

View File

@ -37,8 +37,8 @@ end
function EHeart:lateralKick(dx, piece, new_piece, rot_dir, grid) function EHeart:lateralKick(dx, piece, new_piece, rot_dir, grid)
if (grid:canPlacePiece(new_piece:withOffset({x=dx, y=0}))) then if (grid:canPlacePiece(new_piece:withOffset({x=dx, y=0}))) then
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=0})
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=0})
end end
end end

View File

@ -90,9 +90,9 @@ function PPTPRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end

View File

@ -0,0 +1,23 @@
local ARS = require 'tetris.rulesets.arika'
local Sega = ARS:extend()
Sega.name = "Sega CCW"
Sega.hash = "SegaCCW"
function Sega:attemptRotate(new_inputs, piece, grid)
local rot_dir = 0
if new_inputs["rotate_left"] or new_inputs["rotate_left2"] or
new_inputs["rotate_right"] or new_inputs["rotate_right2"] or
new_inputs["rotate_180"] then rot_dir = 3 end
local new_piece = piece:withRelativeRotation(rot_dir)
if (grid:canPlacePiece(new_piece)) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir)
end
end
return Sega

View File

@ -0,0 +1,76 @@
local SRS = require 'tetris.rulesets.standard_exp'
local OSpin = SRS:extend()
OSpin.name = "SRS O-Spin"
OSpin.hash = "OSpin"
OSpin.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=-1, y=0}, {x=-2, y=0}, {x=-2, y=-1}, {x=-1, y=-1} },
{ {x=-1, y=-1}, {x=-2, y=-1}, {x=-2, y=-2}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=-1, y=-2}, {x=0, y=-2} },
},
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} },
}
}
function OSpin:attemptWallkicks(piece, new_piece, rot_dir, grid)
local kicks
if 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
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
return
end
end
end
return OSpin

View File

@ -1,159 +1,23 @@
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.ruleset' local Ruleset = require 'tetris.rulesets.ti_srs'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
SRS.name = "SRS-X" SRS.name = "SRS-X"
SRS.hash = "Reversed SRS drop functions" SRS.hash = "Reversed SRS drop functions"
SRS.world = true SRS.softdrop_lock = true
SRS.harddrop_lock = false
SRS.spawn_positions = { SRS.colourscheme = {
I = { x=5, y=4 }, I = "R",
J = { x=4, y=5 }, L = "O",
L = { x=4, y=5 }, J = "B",
O = { x=5, y=5 }, S = "M",
S = { x=4, y=5 }, Z = "G",
T = { x=4, y=5 }, O = "Y",
Z = { x=4, y=5 }, T = "C",
} }
SRS.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 },
}
SRS.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} },
}
}
SRS.wallkicks_3x3 = {
[0]={
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[2]={{x=0, y=1}, {x=0, y=-1}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[1]={
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[3]={{x=0, y=1}, {x=0, y=-1}},
},
[2]={
[0]={{x=0, y=1}, {x=0, y=-1}},
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[3]={
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
[1]={{x=0, y=1}, {x=0, y=-1}},
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
},
};
SRS.wallkicks_line = {
[0]={
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
[2]={},
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
},
[1]={
[0]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 2}},
[2]={{x=-1, y= 0}, {x= 2, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
[3]={},
},
[2]={
[0]={},
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 1}},
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}},
},
[3]={
[0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}},
[1]={},
[2]={{x= 1, y= 0}, {x=-2, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
},
};
-- Component functions.
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
local kicks
if piece.shape == "O" then
return
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
function SRS:onPieceCreate(piece, grid)
piece.manipulations = 0
piece.rotations = 0
end
function SRS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset
end
function SRS:onPieceMove(piece, grid) function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then

View File

@ -77,9 +77,9 @@ function Tetra:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end

View File

@ -103,9 +103,9 @@ function TheNext:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end