Add files via upload

pull/1/head
Ishaan Bhardwaj 2020-11-11 22:24:56 -05:00 committed by GitHub
parent ba2fc5cb1d
commit 514170e39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 2530 additions and 0 deletions

3
readme.txt Normal file
View File

@ -0,0 +1,3 @@
Cambridge Modpack v1
To use, simply extract the zip to the root of your Cambridge folder. Enjoy!

39
tetris/modes/4wide.lua Normal file
View File

@ -0,0 +1,39 @@
require 'funcs'
local SurvivalA3Game = require 'tetris.modes.survival_a3'
local FourWideGame = SurvivalA3Game:extend()
FourWideGame.name = "4-wide Simulator"
FourWideGame.hash = "4wide"
FourWideGame.tagline = "The board has gotten narrower! Can you survive the increasing speeds?"
function FourWideGame:initialize(ruleset)
self.super:initialize(ruleset)
self.grid:applyFourWide()
end
local cleared_row_levels = {1, 2, 4, 6}
function FourWideGame:onLineClear(cleared_row_count)
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.roll_frames = -150
else
self.game_over = true
end
else
self.level = math.min(new_level, 1300)
end
self:advanceBottomRow(-cleared_row_count)
end
self.grid:applyFourWide()
end
return FourWideGame

329
tetris/modes/ck.lua Normal file
View File

@ -0,0 +1,329 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SurvivalCKGame = GameMode:extend()
SurvivalCKGame.name = "Survival CK"
SurvivalCKGame.hash = "SurvivalCK"
SurvivalCKGame.tagline = "An endurance mode created by CylinderKnot! Watch out for the fading pieces..."
function SurvivalCKGame:new()
SurvivalCKGame.super:new()
self.garbage = 0
self.roll_frames = 0
self.combo = 1
self.grade = 0
self.level = 0
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
self.coolregret_timer = 0
end
function SurvivalCKGame:getARE()
if self.level < 100 then return 15
elseif self.level < 200 then return 14
elseif self.level < 300 then return 13
elseif self.level < 400 then return 12
elseif self.level < 500 then return 11
elseif self.level < 600 then return 10
elseif self.level < 700 then return 9
elseif self.level < 800 then return 8
elseif self.level < 900 then return 7
elseif self.level < 1000 then return 6
elseif self.level < 2500 then return 5
else return 7 end
end
function SurvivalCKGame:getLineARE()
return SurvivalCKGame:getARE()
end
function SurvivalCKGame:getDasLimit()
if self.level < 700 then return 10
elseif self.level < 900 then return 9
elseif self.level < 1100 then return 8
elseif self.level < 1300 then return 7
elseif self.level < 1600 then return 6
else return 5 end
end
function SurvivalCKGame:getLineClearDelay()
if self.level < 100 then return 10
elseif self.level < 200 then return 8
elseif self.level < 300 then return 7
elseif self.level < 400 then return 6
else return 5 end
end
function SurvivalCKGame:getLockDelay()
if self.level < 600 then return 20
elseif self.level < 700 then return 19
elseif self.level < 800 then return 18
elseif self.level < 900 then return 17
elseif self.level < 1000 then return 16
elseif self.level < 1200 then return 15
elseif self.level < 1400 then return 14
elseif self.level < 1700 then return 13
elseif self.level < 2100 then return 12
elseif self.level < 2200 then return 11
elseif self.level < 2300 then return 10
elseif self.level < 2400 then return 9
elseif self.level < 2500 then return 8
else return 15 end
end
function SurvivalCKGame:getGravity()
return 20
end
function SurvivalCKGame:getGarbageLimit()
if self.level < 1000 then return 20
elseif self.level < 1100 then return 17
elseif self.level < 1200 then return 14
elseif self.level < 1300 then return 11
else return 8 end
end
function SurvivalCKGame:getRegretTime()
if self.level < 500 then return frameTime(0,55)
elseif self.level < 1000 then return frameTime(0,50)
elseif self.level < 1500 then return frameTime(0,40)
elseif self.level < 2000 then return frameTime(0,35)
else return frameTime(0,30) end
end
function SurvivalCKGame:getNextPiece(ruleset)
return {
skin = self.level >= 2000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
local torikan_times = {300, 330, 360, 390, 420, 450, 478, 504, 528, 550, 570}
function SurvivalCKGame:hitTorikan(old_level, new_level)
for i = 1, 11 do
if old_level < (900 + i * 100) and new_level >= (900 + i * 100) and self.frames > torikan_times[i] * 60 then
self.level = 900 + i * 100
return true
end
end
return false
end
function SurvivalCKGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
if self.roll_frames + 1 == 0 then
switchBGM("credit_roll", "gm3")
return true
end
return false
elseif self.roll_frames > 3238 then
switchBGM(nil)
if self.grade ~= 20 then self.grade = self.grade + 1 end
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function SurvivalCKGame:onPieceEnter()
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function SurvivalCKGame:onLineClear(cleared_row_count)
if not self.clear then
local new_level = self.level + cleared_row_count * 2
self:updateSectionTimes(self.level, new_level)
if new_level >= 2500 or self:hitTorikan(self.level, new_level) then
self.clear = true
if new_level >= 2500 then
self.level = 2500
self.grid:clear()
self.big_mode = true
self.roll_frames = -150
end
else
self.level = math.min(new_level, 2500)
end
self:advanceBottomRow(-cleared_row_count)
end
end
function SurvivalCKGame:onPieceLock(piece, cleared_row_count)
self.super:onPieceLock()
if cleared_row_count == 0 then self:advanceBottomRow(1) end
end
function SurvivalCKGame:updateScore(level, drop_bonus, cleared_lines)
if not self.clear then
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
end
function SurvivalCKGame:updateSectionTimes(old_level, new_level)
if math.floor(old_level / 100) < math.floor(new_level / 100) then
local section = math.floor(old_level / 100) + 1
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
if section_time <= self:getRegretTime(self.level) then
self.grade = self.grade + 1
else
self.coolregret_message = "REGRET!!"
self.coolregret_timer = 300
end
end
end
function SurvivalCKGame:advanceBottomRow(dx)
if self.level >= 1000 and self.level < 1500 then
self.garbage = math.max(self.garbage + dx, 0)
if self.garbage >= self:getGarbageLimit() then
self.grid:copyBottomRow()
self.garbage = 0
end
end
end
function SurvivalCKGame:drawGrid()
if self.level >= 1500 and self.level < 1600 then
self.grid:drawInvisible(self.rollOpacityFunction1)
elseif self.level >= 1600 and self.level < 1700 then
self.grid:drawInvisible(self.rollOpacityFunction2)
elseif self.level >= 1700 and self.level < 1800 then
self.grid:drawInvisible(self.rollOpacityFunction3)
elseif self.level >= 1800 and self.level < 1900 then
self.grid:drawInvisible(self.rollOpacityFunction4)
elseif self.level >= 1900 and self.level < 2000 then
self.grid:drawInvisible(self.rollOpacityFunction5)
else
self.grid:draw()
end
end
-- screw trying to make this work efficiently
-- lua function variables are so garbage
SurvivalCKGame.rollOpacityFunction1 = function(age)
if age < 420 then return 1
elseif age > 480 then return 0
else return 1 - (age - 420) / 60 end
end
SurvivalCKGame.rollOpacityFunction2 = function(age)
if age < 360 then return 1
elseif age > 420 then return 0
else return 1 - (age - 360) / 60 end
end
SurvivalCKGame.rollOpacityFunction3 = function(age)
if age < 300 then return 1
elseif age > 360 then return 0
else return 1 - (age - 300) / 60 end
end
SurvivalCKGame.rollOpacityFunction4 = function(age)
if age < 240 then return 1
elseif age > 300 then return 0
else return 1 - (age - 240) / 60 end
end
SurvivalCKGame.rollOpacityFunction5 = function(age)
if age < 180 then return 1
elseif age > 240 then return 0
else return 1 - (age - 180) / 60 end
end
local master_grades = { "M", "MK", "MV", "MO", "MM" }
function SurvivalCKGame:getLetterGrade()
if self.grade == 0 then
return "1"
elseif self.grade < 10 then
return "S" .. tostring(self.grade)
elseif self.grade < 21 then
return "m" .. tostring(self.grade - 9)
elseif self.grade < 26 then
return master_grades[self.grade - 20]
else
return "GM"
end
end
function SurvivalCKGame:drawScoringInfo()
SurvivalCKGame.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("GRADE", text_x, 120, 40, "left")
love.graphics.printf("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
if (self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
self.coolregret_timer = self.coolregret_timer - 1
end
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self:getLetterGrade(self.grade), text_x, 140, 90, "left")
love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 50, "right")
if self.clear then
love.graphics.printf(self.level, text_x, 370, 50, "right")
else
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
end
end
function SurvivalCKGame:getHighscoreData()
return {
grade = self.grade,
level = self.level,
frames = self.frames,
}
end
function SurvivalCKGame:getSectionEndLevel()
return math.floor(self.level / 100 + 1) * 100
end
function SurvivalCKGame:getBackground()
return math.min(math.floor(self.level / 100), 19)
end
return SurvivalCKGame

269
tetris/modes/demon_mode.lua Normal file
View File

@ -0,0 +1,269 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local DemonModeGame = GameMode:extend()
DemonModeGame.name = "Demon Mode"
DemonModeGame.hash = "DemonMode"
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
function DemonModeGame:new()
DemonModeGame.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.grade = 0
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.section_tetris_count = 0
self.section_tries = 0
self.enable_hold = true
self.lock_drop = true
self.lock_hard_drop = true
self.next_queue_length = 3
if math.random() < 1/6.66 then
self.rpc_details = "Suffering"
end
end
function DemonModeGame:getARE()
if self.level < 500 then return 30
elseif self.level < 600 then return 25
elseif self.level < 700 then return 15
elseif self.level < 800 then return 14
elseif self.level < 900 then return 12
elseif self.level < 1000 then return 11
elseif self.level < 1100 then return 10
elseif self.level < 1300 then return 8
elseif self.level < 1400 then return 6
elseif self.level < 1700 then return 4
elseif self.level < 1800 then return 3
elseif self.level < 1900 then return 2
elseif self.level < 2000 then return 1
else return 0 end
end
function DemonModeGame:getLineARE()
return self:getARE()
end
function DemonModeGame:getDasLimit()
if self.level < 500 then return 15
elseif self.level < 1000 then return 10
elseif self.level < 1500 then return 5
elseif self.level < 1700 then return 4
elseif self.level < 1900 then return 3
elseif self.level < 2000 then return 2
else return 1 end
end
function DemonModeGame:getLineClearDelay()
if self.level < 600 then return 15
elseif self.level < 800 then return 10
elseif self.level < 1000 then return 8
elseif self.level < 1500 then return 5
elseif self.level < 1700 then return 3
elseif self.level < 1900 then return 2
elseif self.level < 2000 then return 1
else return 0 end
end
function DemonModeGame:getLockDelay()
if self.level < 100 then return 30
elseif self.level < 200 then return 25
elseif self.level < 300 then return 22
elseif self.level < 400 then return 20
elseif self.level < 1000 then return 15
elseif self.level < 1200 then return 10
elseif self.level < 1400 then return 9
elseif self.level < 1500 then return 8
elseif self.level < 1600 then return 7
elseif self.level < 1700 then return 6
elseif self.level < 1800 then return 5
elseif self.level < 1900 then return 4
elseif self.level < 2000 then return 3
else return 2 end
end
function DemonModeGame:getGravity()
return 20
end
local function getSectionForLevel(level)
return math.floor(level / 100) + 1
end
local cleared_row_levels = {1, 3, 6, 10}
function DemonModeGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames >= 1337 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
end
function DemonModeGame:onPieceEnter()
if (self.level % 100 ~= 99) and self.frames ~= 0 then
self.level = self.level + 1
end
end
function DemonModeGame:onLineClear(cleared_row_count)
if cleared_row_count == 4 then
self.section_tetris_count = self.section_tetris_count + 1
end
local advanced_levels = cleared_row_levels[cleared_row_count]
if not self.clear then
self:updateSectionTimes(self.level, self.level + advanced_levels)
end
end
function DemonModeGame:updateSectionTimes(old_level, new_level)
local section = math.floor(old_level / 100) + 1
if math.floor(old_level / 100) < math.floor(new_level / 100) then
-- If at least one Tetris in this section hasn't been made,
-- deny section passage.
if old_level > 500 then
if self.section_tetris_count == 0 then
self.level = 100 * math.floor(old_level / 100)
self.section_tries = self.section_tries + 1
else
self.level = math.min(new_level, 2500)
-- if this is first try (no denials, add a grade)
if self.section_tries == 0 then
self.grade = self.grade + 1
end
self.section_tries = 0
self.section_tetris_count = 0
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
-- maybe clear
if self.level == 2500 and not self.clear then
self.clear = true
self.grid:clear()
self.roll_frames = -150
end
end
elseif old_level < 100 then
-- If section time is under cutoff, skip to level 500.
if self.frames < frameTime(1,00) then
self.level = 500
self.grade = 5
self.section_tries = 0
self.section_tetris_count = 0
else
self.level = math.min(new_level, 2500)
self.skip_failed = true
self.grade = self.grade + 1
end
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
else
self.level = math.min(new_level, 2500)
if self.skip_failed and new_level >= 500 then
self.level = 500
self.game_over = true
end
self.grade = math.min(self.grade + 1, 4)
end
else
self.level = math.min(new_level, 2500)
end
end
function DemonModeGame:updateScore(level, drop_bonus, cleared_lines)
if not self.clear then
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
end
local letter_grades = {
[0] = "", "D", "C", "B", "A",
"S", "S-A", "S-B", "S-C", "S-D",
"X", "X-A", "X-B", "X-C", "X-D",
"W", "W-A", "W-B", "W-C", "W-D",
"Master", "MasterS", "MasterX", "MasterW", "Grand Master",
"Demon Master"
}
function DemonModeGame:getLetterGrade()
return letter_grades[self.grade]
end
function DemonModeGame:drawGrid()
if self.clear and not (self.completed or self.game_over) then
self.grid:drawInvisible(self.rollOpacityFunction)
else
self.grid:draw()
end
end
DemonModeGame.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function DemonModeGame:drawScoringInfo()
DemonModeGame.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.grade ~= 0 then love.graphics.printf("GRADE", 240, 120, 40, "left") end
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
-- draw section time data
local current_section = getSectionForLevel(self.level)
self:drawSectionTimesWithSecondary(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
love.graphics.printf(string.format("%.2f", self.level / 100), 240, 340, 70, "right")
end
function DemonModeGame:getHighscoreData()
return {
grade = self.grade,
level = self.level,
frames = self.frames,
}
end
function DemonModeGame:getBackground()
return math.min(math.floor(self.level / 100), 19)
end
return DemonModeGame

View File

@ -0,0 +1,161 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local IntervalTrainingGame = GameMode:extend()
IntervalTrainingGame.name = "Interval Training"
IntervalTrainingGame.hash = "IntervalTraining"
IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
function IntervalTrainingGame:new()
self.level = 0
IntervalTrainingGame.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
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 IntervalTrainingGame:initialize(ruleset)
self.section_time_limit = 1800
if ruleset.world then self.section_time_limit = 37 * 60 end
self.super.initialize(self, ruleset)
end
function IntervalTrainingGame:getARE()
return 6
end
function IntervalTrainingGame:getLineARE()
return 6
end
function IntervalTrainingGame:getDasLimit()
return 7
end
function IntervalTrainingGame:getLineClearDelay()
return 4
end
function IntervalTrainingGame:getLockDelay()
return 15
end
function IntervalTrainingGame:getGravity()
return 20
end
function IntervalTrainingGame:getSection()
return math.floor(level / 100) + 1
end
function IntervalTrainingGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 2968 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
if self:getSectionTime() >= self.section_time_limit then
self.game_over = true
end
end
return true
end
function IntervalTrainingGame:onPieceEnter()
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function IntervalTrainingGame:onLineClear(cleared_row_count)
local cleared_level_bonus = {1, 2, 4, 6}
if not self.clear then
local new_level = self.level + cleared_level_bonus[cleared_row_count]
self:updateSectionTimes(self.level, new_level)
self.level = math.min(new_level, 999)
if self.level == 999 then
self.clear = true
end
end
end
function IntervalTrainingGame:getSectionTime()
return self.frames - self.section_start_time
end
function IntervalTrainingGame: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:getSectionTime())
self.section_start_time = self.frames
end
end
function IntervalTrainingGame:drawGrid()
self.grid:draw()
end
function IntervalTrainingGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function IntervalTrainingGame: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")
if not self.clear then love.graphics.printf("TIME LEFT", 240, 250, 80, "left") end
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")
-- 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 time_left < frameTime(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
if not self.clear then love.graphics.printf(formatTime(time_left), 240, 270, 160, "left") end
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
end
function IntervalTrainingGame:getSectionEndLevel()
if self.level >= 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function IntervalTrainingGame:getBackground()
return math.floor(self.level / 100)
end
return IntervalTrainingGame

192
tetris/modes/konoha.lua Normal file
View File

@ -0,0 +1,192 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local KonohaRandomizer = require 'tetris.randomizers.bag_konoha'
local KonohaGame = GameMode:extend()
KonohaGame.name = "All Clear A4"
KonohaGame.hash = "AllClearA4"
KonohaGame.tagline = "Get as many bravos as you can under the time limit!"
function KonohaGame:new()
KonohaGame.super:new()
self.randomizer = KonohaRandomizer()
self.bravos = 0
self.last_bonus_amount = 0
self.last_bonus_display_time = 0
self.time_limit = 10800
self.big_mode = true
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function KonohaGame:getARE()
if self.level < 300 then return 30
elseif self.level < 400 then return 25
elseif self.level < 500 then return 20
elseif self.level < 600 then return 17
elseif self.level < 800 then return 15
elseif self.level < 900 then return 13
elseif self.level < 1000 then return 10
elseif self.level < 1300 then return 8
else return 6 end
end
function KonohaGame:getLineARE()
return self:getARE()
end
function KonohaGame:getDasLimit()
if self.level < 500 then return 10
elseif self.level < 800 then return 9
elseif self.level < 1000 then return 8
else return 7 end
end
function KonohaGame:getLineClearDelay()
if self.level < 200 then return 14
elseif self.level < 500 then return 9
elseif self.level < 800 then return 8
elseif self.level < 1000 then return 7
else return 6 end
end
function KonohaGame:getLockDelay()
if self.level < 500 then return 30
elseif self.level < 600 then return 25
elseif self.level < 700 then return 23
elseif self.level < 800 then return 20
elseif self.level < 900 then return 17
elseif self.level < 1000 then return 15
elseif self.level < 1200 then return 13
elseif self.level < 1300 then return 10
else return 8 end
end
function KonohaGame:getGravity()
if (self.level < 30) then return 4/256
elseif (self.level < 35) then return 8/256
elseif (self.level < 40) then return 12/256
elseif (self.level < 50) then return 16/256
elseif (self.level < 60) then return 32/256
elseif (self.level < 70) then return 48/256
elseif (self.level < 80) then return 64/256
elseif (self.level < 90) then return 128/256
elseif (self.level < 100) then return 192/256
elseif (self.level < 120) then return 1
elseif (self.level < 140) then return 2
elseif (self.level < 160) then return 3
elseif (self.level < 170) then return 4
elseif (self.level < 200) then return 5
else return 20 end
end
function KonohaGame:getSection()
return math.floor(level / 100) + 1
end
function KonohaGame:getSectionEndLevel()
return math.floor(self.level / 100 + 1) * 100
end
function KonohaGame:advanceOneFrame()
if self.ready_frames == 0 then
self.time_limit = self.time_limit - 1
self.frames = self.frames + 1
end
if self.time_limit <= 0 then
self.game_over = true
end
self.last_bonus_display_time = self.last_bonus_display_time - 1
end
function KonohaGame:onPieceEnter()
if (self.level % 100 ~= 99) and self.frames ~= 0 then
self.level = self.level + 1
end
end
function KonohaGame:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
local cleared_row_levels = {2, 4, 6, 12}
local bravo_bonus = {300, 480, 660, 900}
local non_bravo_bonus = {0, 0, 20, 40}
local bravo_ot_bonus = {0, 60, 120, 180}
function KonohaGame:onLineClear(cleared_row_count)
local oldtime = self.time_limit
self.level = self.level + cleared_row_levels[cleared_row_count / 2]
if self.grid:checkForBravo(cleared_row_count) then
self.bravos = self.bravos + 1
if self.level < 1000 then self.time_limit = self.time_limit + bravo_bonus[cleared_row_count / 2]
else self.time_limit = self.time_limit + bravo_ot_bonus[cleared_row_count / 2]
end
if self.bravos == 11 then self.randomizer.allowrepeat = true end
elseif self.level < 1000 then
self.time_limit = self.time_limit + non_bravo_bonus[cleared_row_count / 2]
end
local bonus = self.time_limit - oldtime
if bonus > 0 then
self.last_bonus_amount = bonus
self.last_bonus_display_time = 120
end
end
function KonohaGame:getBackground()
return math.floor(self.level / 100)
end
function KonohaGame: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 LIMIT", 240, 120, 120, "left")
love.graphics.printf("BRAVOS", 240, 200, 50, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
if not self.game_over and self.time_limit < frameTime(0,10) and self.time_limit % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(self.time_limit), 240, 140, 120, "right")
love.graphics.setColor(1, 1, 1, 1)
if self.last_bonus_display_time > 0 then
love.graphics.printf("+"..formatTime(self.last_bonus_amount), 240, 160, 120, "right")
end
love.graphics.printf(self.bravos, 240, 220, 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 KonohaGame:getHighscoreData()
return {
bravos = self.bravos,
level = self.level,
frames = self.frames,
}
end
return KonohaGame

View File

@ -0,0 +1,150 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local SegaRandomizer = require 'tetris.randomizers.sega'
local MarathonC88Game = GameMode:extend()
MarathonC88Game.name = "Marathon C88"
MarathonC88Game.hash = "MarathonC88"
MarathonC88Game.tagline = "An old Japanese game! Can you hit the max score?"
function MarathonC88Game:new()
self.super:new()
self.level_timer = 0
self.level_lines = 0
self.last_cleared = 0
self.randomizer = SegaRandomizer()
self.lock_drop = false
self.enable_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
self.grid:applyCeiling(4)
end
function MarathonC88Game:getARE() return 30 end
function MarathonC88Game:getLineARE() return 30 end
function MarathonC88Game:getDasLimit() return 20 end
function MarathonC88Game:getLineClearDelay() return 42 end
function MarathonC88Game:getLockDelay() return 30 end
function MarathonC88Game:getGravity()
if self.level == 0 then return 1/30
elseif self.level == 1 then return 1/15
elseif self.level == 2 then return 1/12
elseif self.level == 3 then return 1/10
elseif self.level == 4 then return 1/8
elseif self.level == 5 then return 1/6
elseif self.level == 6 then return 1/4
elseif self.level == 7 then return 1/2
elseif self.level <= 9 then return 1
elseif self.level == 10 then return 1/8
elseif self.level == 11 then return 1/6
elseif self.level == 12 then return 1/4
elseif self.level == 13 then return 1/2
else return 1 end
end
function MarathonC88Game:getLevelTimerLimit()
if self.level == 0 then return 3480
elseif self.level <= 8 then return 2320
elseif self.level <= 10 then return 3480
elseif self.level <= 14 then return 1740
else return 3480 end
end
function MarathonC88Game:getScoreMultiplier()
if self.level <= 1 then return 1
elseif self.level <= 3 then return 2
elseif self.level <= 5 then return 3
elseif self.level <= 7 then return 4
else return 5 end
end
function MarathonC88Game:advanceOneFrame()
if not (self.piece == nil and self.level_timer == 0) and self.ready_frames == 0 then
self.level_timer = self.level_timer + 1
end
if self.ready_frames == 0 then self.frames = self.frames + 1 end
if self.drop_bonus > 0 then
self.score = self.score + self.drop_bonus * self:getScoreMultiplier()
self.drop_bonus = 0
end
end
function MarathonC88Game:onPieceEnter()
for row = 5, 4 + self.last_cleared do self.grid:clearSpecificRow(row) end
self.grid:applyCeiling(4)
end
local score_table = {[0] = 0, 1, 4, 9, 20}
function MarathonC88Game:updateScore(level, drop_bonus, cleared_lines)
local bravo = self.grid:checkForBravo(cleared_lines) and 10 or 1
self.score = self.score + score_table[cleared_lines] * 100 * self:getScoreMultiplier() * bravo
self.level_lines = self.level_lines + cleared_lines
self.lines = self.lines + cleared_lines
if (cleared_lines == 0 and self.level_timer >= self:getLevelTimerLimit()) or (self.level_lines >= 4) then
self.level = self.level + 1
self.level_lines = 0
self.level_timer = 0
end
self.last_cleared = cleared_lines
end
MarathonC88Game.opacityFunction = function(age)
return 1
end
MarathonC88Game.ceilingOpacityFunction = function(age)
return 0
end
function MarathonC88Game:drawGrid()
self.grid:drawInvisible(self.opacityFunction, self.ceilingOpacityFunction)
end
function MarathonC88Game:drawScoringInfo()
MarathonC88Game.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")
love.graphics.printf("SCORE", 240, 120, 40, "left")
love.graphics.printf("LINES", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 280, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 140, 90, "left")
love.graphics.printf(self.lines, 240, 220, 90, "left")
love.graphics.printf(self.level, 240, 300, 90, "left")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonC88Game:getBackground()
return math.min(math.floor(self.level / 2), 19)
end
function MarathonC88Game:getHighscoreData()
return {
score = self.score,
level = self.level,
lines = self.lines,
frames = self.frames,
}
end
return MarathonC88Game

View File

@ -0,0 +1,186 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Randomizer = require 'tetris.randomizers.randomizer'
local MarathonC89Game = GameMode:extend()
MarathonC89Game.name = "Marathon C89"
MarathonC89Game.hash = "MarathonC89"
MarathonC89Game.tagline = "Can you play fast enough to reach the killscreen?"
function MarathonC89Game:new()
MarathonC89Game.super:new()
self.randomizer = Randomizer()
self.ready_frames = 1
self.waiting_frames = 72
self.start_level = 12
self.level = 12
self.lock_drop = true
self.enable_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
self.additive_gravity = false
end
function MarathonC89Game:getDropSpeed() return 1/2 end
function MarathonC89Game:getDasLimit() return 16 end
function MarathonC89Game:getARR() return 6 end
function MarathonC89Game:getARE() return 6 end
function MarathonC89Game:getLineARE() return 6 end
function MarathonC89Game:getLineClearDelay() return 30 end
function MarathonC89Game:getLockDelay() return 0 end
function MarathonC89Game:chargeDAS(inputs)
if inputs[self.das.direction] == true and
self.prev_inputs[self.das.direction] == true and
not inputs["down"] and
self.piece ~= nil
then
local das_frames = self.das.frames + 1
if das_frames >= self:getDasLimit() then
if self.das.direction == "left" then
self.move = (self:getARR() == 0 and "speed" or "") .. "left"
self.das.frames = self:getDasLimit() - self:getARR()
elseif self.das.direction == "right" then
self.move = (self:getARR() == 0 and "speed" or "") .. "right"
self.das.frames = self:getDasLimit() - self:getARR()
end
else
self.move = "none"
self.das.frames = das_frames
end
elseif inputs["right"] == true then
self.das.direction = "right"
if not inputs["down"] and self.piece ~= nil then
self.move = "right"
self.das.frames = 0
else
self.move = "none"
end
elseif inputs["left"] == true then
self.das.direction = "left"
if not inputs["down"] and self.piece ~= nil then
self.move = "left"
self.das.frames = 0
else
self.move = "none"
end
else
self.move = "none"
end
if self.das.direction == "left" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=-1, y=0}) or
self.das.direction == "right" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=1, y=0})
then
self.das.frames = self:getDasLimit()
end
if inputs["down"] == false and self.prev_inputs["down"] == true then
self.drop_bonus = 0
end
end
local gravity_table = {
[0] =
1366/65536, 1525/65536, 1725/65536, 1986/65536, 2341/65536,
2850/65536, 3641/65536, 5042/65536, 8192/65536, 10923/65536,
13108/65536, 13108/65536, 13108/65536, 16384/65536, 16384/65536,
16384/65536, 21846/65536, 21846/65536, 21846/65536
}
function MarathonC89Game:getGravity()
if self.waiting_frames > 0 then return 0 end
if self.level >= 29 then return 1
elseif self.level >= 19 then return 1/2
else return gravity_table[self.level] end
end
function MarathonC89Game:advanceOneFrame()
if self.waiting_frames > 0 then
self.waiting_frames = self.waiting_frames - 1
else
self.frames = self.frames + 1
end
return true
end
function MarathonC89Game:onPieceLock()
self.super:onPieceLock()
self.score = self.score + self.drop_bonus
self.drop_bonus = 0
end
local cleared_line_scores = { 40, 100, 300, 1200 }
function MarathonC89Game:getLevelForLines()
if self.start_level < 10 then
return math.max(self.start_level, math.floor(self.lines / 10))
elseif self.start_level < 16 then
return math.max(self.start_level, self.start_level + math.floor((self.lines - 100) / 10))
else
return math.max(self.start_level, math.floor((self.lines - 60) / 10))
end
end
function MarathonC89Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + cleared_line_scores[cleared_lines] * (self.level + 1)
self.lines = self.lines + cleared_lines
self.level = self:getLevelForLines()
else
self.drop_bonus = 0
self.combo = 1
end
end
function MarathonC89Game:drawGrid()
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
function MarathonC89Game:drawScoringInfo()
MarathonC89Game.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")
love.graphics.printf("LINES", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.lines, 240, 140, 90, "left")
love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonC89Game:getBackground()
return math.min(self.level, 19)
end
function MarathonC89Game:getHighscoreData()
return {
score = self.score,
level = self.level,
}
end
return MarathonC89Game

View File

@ -0,0 +1,22 @@
local PhantomManiaGame = require 'tetris.modes.phantom_mania'
local PhantomManiaNGame = PhantomManiaGame:extend()
PhantomManiaNGame.name = "Phantom Mania N"
PhantomManiaNGame.hash = "PhantomManiaN"
PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS support."
function PhantomManiaNGame:new()
PhantomManiaNGame.super:new()
self.SGnames = {
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
"M10", "M11", "M12", "M13", "M14", "M15", "M16", "M17", "M18",
"GM"
}
self.next_queue_length = 3
self.enable_hold = true
end
return PhantomManiaNGame

151
tetris/modes/race_40.lua Normal file
View File

@ -0,0 +1,151 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
local Race40Game = GameMode:extend()
Race40Game.name = "Race 40"
Race40Game.hash = "Race40"
Race40Game.tagline = "How fast can you clear 40 lines?"
function Race40Game:new()
Race40Game.super:new()
self.lines = 0
self.line_goal = 40
self.pieces = 0
self.randomizer = Bag7Randomiser()
self.roll_frames = 0
self.SGnames = {
[0] = "",
"9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM"
}
self.upstacked = false
self.lock_drop = true
self.lock_hard_drop = true
self.instant_hard_drop = true
self.instant_soft_drop = false
self.enable_hold = true
self.next_queue_length = 3
end
function Race40Game:getDropSpeed()
return 20
end
function Race40Game:getARR()
return 1
end
function Race40Game:getARE()
return 0
end
function Race40Game:getLineARE()
return self:getARE()
end
function Race40Game:getDasLimit()
return 10
end
function Race40Game:getLineClearDelay()
return 0
end
function Race40Game:getLockDelay()
return 30
end
function Race40Game:getGravity()
return 1/64
end
function Race40Game:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 150 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function Race40Game:onPieceLock()
self.super:onPieceLock()
self.pieces = self.pieces + 1
end
function Race40Game:onLineClear(cleared_row_count)
if not self.clear then
self.lines = self.lines + cleared_row_count
if self.lines >= self.line_goal then
self.clear = true
end
end
end
function Race40Game:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function Race40Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function Race40Game:getSecretGrade(sg)
if sg == 19 then self.upstacked = true end
if self.upstacked then return self.SGnames[14 + math.floor((20 - sg) / 4)]
else return self.SGnames[math.floor((sg / 19) * 14)] end
end
function Race40Game:drawScoringInfo()
Race40Game.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, 320, 40, "left")
love.graphics.printf("line/min", text_x, 160, 80, "left")
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
local sg = self.grid:checkSecretGrade()
if sg >= 7 or self.upstacked then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
if sg >= 7 or self.upstacked then
love.graphics.printf(self:getSecretGrade(sg), 240, 450, 180, "left")
end
love.graphics.setFont(font_3x5_4)
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
end
function Race40Game:getBackground()
return 2
end
return Race40Game

189
tetris/modes/scoredrain.lua Normal file
View File

@ -0,0 +1,189 @@
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")
local frames_left = self.score / self.drain_rate * 60
if frames_left <= 600 and frames_left % 4 < 2 and not self.game_over then love.graphics.setColor(1, 0.3, 0.3, 1) end
love.graphics.printf(formatTime(frames_left), 240, 270, 120, "left")
love.graphics.setColor(1, 1, 1, 1)
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

214
tetris/modes/tgmplus.lua Normal file
View File

@ -0,0 +1,214 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local TGMPlusGame = GameMode:extend()
TGMPlusGame.name = "Marathon A2+"
TGMPlusGame.hash = "A2Plus"
TGMPlusGame.tagline = "The garbage rises steadily! Can you make it to level 999?"
function TGMPlusGame:new()
TGMPlusGame.super:new()
self.roll_frames = 0
self.combo = 1
self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM"
}
self.randomizer = History6RollsRandomizer()
self.lock_drop = false
self.lock_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
self.garbage_queue = 0
self.garbage_pos = 0
self.garbage_rows = {
[0] =
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"e", "e", "b", "b", "b", "b", "b", "b", "b", "b"},
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
{"b", "b", "b", "b", "b", "b", "b", "b", "e", "e"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
{"b", "b", "e", "b", "b", "b", "b", "b", "b", "b"},
{"b", "e", "e", "b", "b", "b", "b", "b", "b", "b"},
{"b", "e", "b", "b", "b", "b", "b", "b", "b", "b"},
{"b", "b", "b", "b", "b", "b", "b", "e", "b", "b"},
{"b", "b", "b", "b", "b", "b", "b", "e", "e", "b"},
{"b", "b", "b", "b", "b", "b", "b", "b", "e", "b"},
{"b", "b", "b", "b", "e", "e", "b", "b", "b", "b"},
{"b", "b", "b", "b", "e", "e", "b", "b", "b", "b"},
{"b", "b", "b", "b", "e", "b", "b", "b", "b", "b"},
{"b", "b", "b", "e", "e", "e", "b", "b", "b", "b"},
}
end
function TGMPlusGame:getARE() return 25 end
function TGMPlusGame:getDasLimit() return 15 end
function TGMPlusGame:getLockDelay() return 30 end
function TGMPlusGame:getLineClearDelay() return 40 end
function TGMPlusGame: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 TGMPlusGame:getGarbageLimit() return 13 - math.floor(self.level / 100) end
function TGMPlusGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 3694 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function TGMPlusGame:onPieceEnter()
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function TGMPlusGame:onPieceLock(piece, cleared_row_count)
self.super:onPieceLock()
if cleared_row_count == 0 then self:advanceBottomRow() end
end
function TGMPlusGame:onLineClear(cleared_row_count)
self.level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 and not self.clear then self.clear = true end
self.lock_drop = self.level >= 900
self.lock_hard_drop = self.level >= 900
end
function TGMPlusGame:advanceBottomRow()
self.garbage_queue = self.garbage_queue + 1
if self.garbage_queue >= self:getGarbageLimit() then
self.grid:garbageRise(self.garbage_rows[self.garbage_pos])
self.garbage_queue = 0
self.garbage_pos = (self.garbage_pos + 1) % 24
end
end
function TGMPlusGame: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
)
else
self.combo = 1
end
self.drop_bonus = 0
end
end
function TGMPlusGame:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
function TGMPlusGame:getHighscoreData()
return {
score = self.score,
level = self.level,
frames = self.frames,
}
end
function TGMPlusGame:getSectionEndLevel()
if self.level >= 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function TGMPlusGame:getBackground()
return math.floor(self.level / 100)
end
function TGMPlusGame: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("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(self:getSectionEndLevel(), 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 TGMPlusGame

View File

@ -0,0 +1,17 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag5Randomizer = Randomizer:extend()
function Bag5Randomizer:initialize()
self.bag = {"I", "J", "L", "O", "T"}
end
function Bag5Randomizer:generatePiece()
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x)
end
return Bag5Randomizer

View File

@ -0,0 +1,24 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag5AltRandomizer = Randomizer:extend()
function Bag5AltRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "T"}
self.prev = nil
end
function Bag5AltRandomizer:generatePiece()
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(table.getn(self.bag))
local temp = table.remove(self.bag, x)
if temp == self.prev then
local y = math.random(table.getn(self.bag))
temp = table.remove(self.bag, y)
end
self.prev = temp
return temp
end
return Bag5AltRandomizer

View File

@ -0,0 +1,24 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7Randomizer = Randomizer:extend()
function Bag7Randomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
end
function Bag7Randomizer:generatePiece()
if next(self.extra) == nil then
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
end
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
end
local x = math.random(table.getn(self.bag))
--print("Bag: "..table.concat(self.bag, ", ").." | Extra: "..table.concat(self.extra, ", "))
return table.remove(self.bag, x)
end
return Bag7Randomizer

View File

@ -0,0 +1,28 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local BagKonoha = Randomizer:extend()
function BagKonoha:initialize()
self.bag = {"I", "J", "L", "O", "T"}
self.prev = nil
self.allowrepeat = false
self.generated = 0
end
function BagKonoha:generatePiece()
self.generated = self.generated + 1
if #self.bag == 0 then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(#self.bag)
local temp = table.remove(self.bag, x)
if temp == self.prev and not self.allowrepeat then
local y = math.random(#self.bag)
table.insert(self.bag, temp) -- should insert at the end of the bag, bag[y] doesnt change
temp = table.remove(self.bag, y)
end
self.prev = temp
return temp
end
return BagKonoha

View File

@ -0,0 +1,30 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local RecursiveRandomizer = Randomizer:extend()
function RecursiveRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
end
function RecursiveRandomizer:generatePiece()
--if next(self.bag) == nil then
-- self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
--end
local x = math.random(table.getn(self.bag) + 1)
while x == table.getn(self.bag) + 1 do
--print("Refill piece pulled")
table.insert(self.bag, "I")
table.insert(self.bag, "J")
table.insert(self.bag, "L")
table.insert(self.bag, "O")
table.insert(self.bag, "S")
table.insert(self.bag, "T")
table.insert(self.bag, "Z")
x = math.random(table.getn(self.bag) + 1)
end
--print("Number of pieces in bag: "..table.getn(self.bag))
--print("Bag: "..table.concat(self.bag, ", "))
return table.remove(self.bag, x)
end
return RecursiveRandomizer

View File

@ -0,0 +1,19 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local SegaRandomizer = Randomizer:extend()
function SegaRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
self.sequence = {}
for i = 1, 1000 do
self.sequence[i] = self.bag[math.random(table.getn(self.bag))]
end
self.counter = 0
end
function SegaRandomizer:generatePiece()
self.counter = self.counter + 1
return self.sequence[self.counter % 1000 + 1]
end
return SegaRandomizer

173
tetris/rulesets/crap.lua Normal file
View File

@ -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 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)
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

153
tetris/rulesets/dtet.lua Normal file
View File

@ -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

View File

@ -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

102
tetris/rulesets/pptprs.lua Normal file
View File

@ -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

10
tetris/rulesets/sega.lua Normal file
View File

@ -0,0 +1,10 @@
local ARS = require 'tetris.rulesets.arika'
local Sega = ARS:extend()
Sega.name = "Sega Rotation"
Sega.hash = "Sega"
function Sega:attemptWallkicks() end
return Sega