First bundled release.

This commit is contained in:
Joe Z
2019-05-22 23:57:34 -04:00
commit c973929e0c
120 changed files with 8709 additions and 0 deletions

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

@@ -0,0 +1,258 @@
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?"
DemonModeGame.arr = 1
DemonModeGame.drop_speed = 1
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.next_queue_length = 3
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
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 < sp(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)
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
end
else
self.level = math.min(new_level, 2500)
end
end
function DemonModeGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
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 .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("GRADE", 240, 120, 40, "left")
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.floor(self.level / 100)
end
return DemonModeGame

442
tetris/modes/gamemode.lua Normal file
View File

@@ -0,0 +1,442 @@
local Object = require 'libs.classic'
require 'funcs'
local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer'
local GameMode = Object:extend()
GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new()
self.grid = Grid()
self.randomizer = Randomizer()
self.piece = nil
self.ready_frames = 100
self.frames = 0
self.game_over_frames = 0
self.score = 0
self.level = 0
self.lines = 0
self.drop_bonus = 0
self.are = 0
self.lcd = 0
self.das = { direction = "none", frames = -1 }
self.move = "none"
self.prev_inputs = {}
self.next_queue = {}
self.game_over = false
self.clear = false
self.completed = false
-- configurable parameters
self.lock_drop = false
self.lock_hard_drop = false
self.instant_hard_drop = false
self.instant_soft_drop = true
self.enable_hold = false
self.enable_hard_drop = true
self.next_queue_length = 1
self.draw_section_times = false
self.draw_secondary_section_times = false
-- variables related to configurable parameters
self.drop_locked = false
self.hard_drop_locked = false
self.hold_queue = nil
self.held = false
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.secondary_section_times = { [0] = 0 }
end
function GameMode:initialize()
-- after all the variables are initialized, run initialization procedures
for i = 1, 30 do
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
end
function GameMode:getARR() return 1 end
function GameMode:getDropSpeed() return 1 end
function GameMode:getARE() return 25 end
function GameMode:getLineARE() return 25 end
function GameMode:getLockDelay() return 30 end
function GameMode:getLineClearDelay() return 40 end
function GameMode:getDasLimit() return 15 end
function GameMode:getNextPiece(ruleset)
return {
skin = "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function GameMode:initialize(ruleset)
-- generate next queue
for i = 1, self.next_queue_length do
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
end
function GameMode:update(inputs, ruleset)
if self.game_over then
self.game_over_frames = self.game_over_frames + 1
if self.game_over_frames >= 60 then
self.completed = true
end
return
end
if self.completed then return end
-- advance one frame
if self:advanceOneFrame(inputs) == false then return end
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
if self.piece == nil then
self:processDelays(inputs, ruleset)
else
-- perform active frame actions such as fading out the next queue
self:whilePieceActive()
local gravity = self:getGravity()
if self.enable_hold and inputs["hold"] == true and self.held == false then
self:hold(inputs, ruleset)
self.prev_inputs = inputs
return
end
if self.lock_drop and inputs["down"] ~= true then
self.drop_locked = false
end
if self.lock_hard_drop and inputs["up"] ~= true then
self.hard_drop_locked = false
end
ruleset:processPiece(
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
self.move, self:getLockDelay(), self:getDropSpeed(),
self.drop_locked, self.hard_drop_locked, self.enable_hard_drop
)
if inputs["up"] == true and
self.piece:isDropBlocked(self.grid) and
not self.hard_drop_locked and
self.instant_hard_drop
then
self.piece.locked = true
end
if inputs["down"] == true and
self.piece:isDropBlocked(self.grid) and
not self.drop_locked and
self.instant_soft_drop
then
self.piece.locked = true
end
if self.piece.locked == true then
self.grid:applyPiece(self.piece)
self:onPieceLock(self.piece)
self.piece = nil
if self.enable_hold then
self.held = false
end
self.grid:markClearedRows()
local cleared_row_count = self.grid:getClearedRowCount()
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
if cleared_row_count > 0 then
self.lcd = self:getLineClearDelay()
self.are = self:getLineARE()
if self.lcd == 0 then
self.grid:clearClearedRows()
if self.are == 0 then
self:initializeOrHold(inputs, ruleset)
end
end
self:onLineClear(cleared_row_count)
else
if self:getARE() == 0 then
self:initializeOrHold(inputs, ruleset)
else
self.are = self:getARE()
end
end
end
end
self.prev_inputs = inputs
end
function GameMode:updateScore() end
function GameMode:advanceOneFrame()
if self.clear then
self.completed = true
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
end
-- event functions
function GameMode:whilePieceActive() end
function GameMode:onPieceLock(piece) end
function GameMode:onLineClear(cleared_row_count) end
function GameMode:onPieceEnter() end
function GameMode:onHold() end
function GameMode:onGameOver()
switchBGM(nil)
end
function GameMode:chargeDAS(inputs)
if inputs[self.das.direction] == true 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.move = "right"
self.das = { direction = "right", frames = 0 }
elseif inputs["left"] == true then
self.move = "left"
self.das = { direction = "left", frames = 0 }
else
self.move = "none"
self.das = { direction = "none", frames = -1 }
end
end
function GameMode:processDelays(inputs, ruleset, drop_speed)
if self.ready_frames > 0 then
self.ready_frames = self.ready_frames - 1
if self.ready_frames == 0 then
self:initializeOrHold(inputs, ruleset)
end
elseif self.lcd > 0 then
self.lcd = self.lcd - 1
if self.lcd == 0 then
self.grid:clearClearedRows()
if self.are == 0 then
self:initializeOrHold(inputs, ruleset)
end
end
elseif self.are > 0 then
self.are = self.are - 1
if self.are == 0 then
self:initializeOrHold(inputs, ruleset)
end
end
end
function GameMode:initializeOrHold(inputs, ruleset)
if self.enable_hold and inputs["hold"] == true then
self:hold(inputs, ruleset)
else
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
end
self:onPieceEnter()
if not self.grid:canPlacePiece(self.piece) then
self:onGameOver()
self.game_over = true
end
end
function GameMode:hold(inputs, ruleset)
local data = copy(self.hold_queue)
if self.piece == nil then
self.hold_queue = self.next_queue[1]
table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset))
else
self.hold_queue = {
skin = self.piece.skin,
shape = self.piece.shape,
orientation = ruleset:getDefaultOrientation(),
}
end
if data == nil then
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
else
self:initializeNextPiece(inputs, ruleset, data, false)
end
self.held = true
self:onHold()
end
function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next_piece)
local gravity = self:getGravity()
self.piece = ruleset:initializePiece(
inputs, piece_data, self.grid, gravity,
self.prev_inputs, self.move,
self:getLockDelay(), self:getDropSpeed(),
self.lock_drop, self.lock_hard_drop
)
if self.lock_drop then
self.drop_locked = true
end
if self.lock_hard_drop then
self.hard_drop_locked = true
end
if generate_next_piece == nil then
table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
self:playNextSound()
end
function GameMode:playNextSound()
playSE("blocks", self.next_queue[1].shape)
end
function GameMode:getHighScoreData()
return {
score = self.score
}
end
function GameMode:drawPiece()
if self.piece ~= nil then
self.piece:draw(
1,
self:getLockDelay() == 0 and 1 or
(0.25 + 0.75 * math.max(1 - self.piece.gravity, 1 - (self.piece.lock_delay / self:getLockDelay()))),
self.grid
)
end
end
function GameMode:drawGhostPiece(ruleset)
if self.piece == nil then return end
local ghost_piece = self.piece:withOffset({x=0, y=0})
ghost_piece.ghost = true
ghost_piece:dropToBottom(self.grid)
ghost_piece:draw(0.5)
end
function GameMode:drawNextQueue(ruleset)
function drawPiece(piece, skin, offsets, pos_x, pos_y)
for index, offset in pairs(offsets) do
local x = ruleset.spawn_positions[piece].x + offset.x
local y = ruleset.spawn_positions[piece].y + offset.y
love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16)
end
end
for i = 1, self.next_queue_length do
self:setNextOpacity(i)
local next_piece = self.next_queue[i].shape
local skin = self.next_queue[i].skin
local rotation = self.next_queue[i].orientation
if config.side_next then -- next at side
drawPiece(next_piece, skin, ruleset.block_offsets[next_piece][rotation], 192, -16+i*48)
else -- next at top
drawPiece(next_piece, skin, ruleset.block_offsets[next_piece][rotation], -16+i*80, -32)
end
end
if self.hold_queue ~= nil then
self:setHoldOpacity()
drawPiece(
self.hold_queue.shape,
self.hold_queue.skin,
ruleset.block_offsets[self.hold_queue.shape][self.hold_queue.orientation],
-16, -32
)
end
return false
end
function GameMode:setNextOpacity(i) love.graphics.setColor(1, 1, 1, 1) end
function GameMode:setHoldOpacity() love.graphics.setColor(1, 1, 1, 1) end
function GameMode:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
if config["side_next"] then
love.graphics.printf("NEXT", 240, 72, 40, "left")
else
love.graphics.printf("NEXT", 64, 40, 40, "left")
end
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
st(self.prev_inputs)
)
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function GameMode:drawSectionTimes(current_section)
local section_x = 530
for section, time in pairs(self.section_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
end
end
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
end
function GameMode:drawSectionTimesWithSecondary(current_section)
local section_x = 530
local section_secondary_x = 440
for section, time in pairs(self.section_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
end
end
for section, time in pairs(self.secondary_section_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_secondary_x, 40 + 20 * section, 90, "left")
end
end
local current_x
if table.getn(self.section_times) < table.getn(self.secondary_section_times) then
current_x = section_x
else
current_x = section_secondary_x
end
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
end
function GameMode:drawSectionTimesWithSplits(current_section)
local section_x = 440
local split_x = 530
local split_time = 0
for section, time in pairs(self.section_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
split_time = split_time + time
love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left")
end
end
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left")
end
function GameMode:drawCustom() end
return GameMode

View File

@@ -0,0 +1,155 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
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?"
IntervalTrainingGame.arr = 1
IntervalTrainingGame.drop_speed = 1
function IntervalTrainingGame:new()
IntervalTrainingGame.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.section_time_limit = 1800
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function IntervalTrainingGame:getARE()
return 4
end
function IntervalTrainingGame:getLineARE()
return 4
end
function IntervalTrainingGame:getDasLimit()
return 6
end
function IntervalTrainingGame:getLineClearDelay()
return 6
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
return false
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 or 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)
if not self.clear then
local new_level = self.level + 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
else
self.level = math.min(new_level, 999)
end
end
function IntervalTrainingGame:drawGrid(ruleset)
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 .. " " ..
st(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")
-- 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 < sp(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
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

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

@@ -0,0 +1,131 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local LigneGame = GameMode:extend()
LigneGame.name = "Ligne"
LigneGame.hash = "Ligne"
LigneGame.tagline = "How fast can you clear 40 lines?"
LigneGame.arr = 1
LigneGame.drop_speed = 1
function LigneGame:new()
LigneGame.super:new()
self.lines = 0
self.line_goal = 40
self.pieces = 0
self.randomizer = History6RollsRandomizer()
self.roll_frames = 0
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 LigneGame:getDropSpeed()
return 20
end
function LigneGame:getARR()
return 0
end
function LigneGame:getARE()
return 0
end
function LigneGame:getLineARE()
return self:getARE()
end
function LigneGame:getDasLimit()
return 6
end
function LigneGame:getLineClearDelay()
return 0
end
function LigneGame:getLockDelay()
return 15
end
function LigneGame:getGravity()
return 1/64
end
function LigneGame: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 LigneGame:onPieceLock()
self.pieces = self.pieces + 1
end
function LigneGame: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 LigneGame:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function LigneGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function LigneGame:drawScoringInfo()
LigneGame.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")
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")
love.graphics.setFont(font_3x5_4)
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
end
function LigneGame:getBackground()
return 2
end
return LigneGame

View File

@@ -0,0 +1,446 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local Marathon2020Game = GameMode:extend()
Marathon2020Game.name = "Marathon 2020"
Marathon2020Game.hash = "Marathon2020"
Marathon2020Game.tagline = "2020 levels of pure pain! Can you achieve the World Master rank?"
function Marathon2020Game:new()
Marathon2020Game.super:new()
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
self.delay_level = 0
self.roll_frames = 0
self.no_roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.section_cool_count = 0
self.section_status = { [0] = "none" }
self.torikan_passed = {
[500] = false, [900] = false,
[1000] = false, [1500] = false, [1900] = false
}
self.torikan_hit = false
self.grade = 0
self.grade_points = 0
self.grade_point_decay_counter = 0
self.max_grade_points = 0
end
function Marathon2020Game:getARE()
if self.delay_level < 1 then return 27
elseif self.delay_level < 2 then return 24
elseif self.delay_level < 3 then return 21
elseif self.delay_level < 4 then return 18
elseif self.delay_level < 5 then return 16
elseif self.delay_level < 6 then return 14
elseif self.delay_level < 7 then return 12
elseif self.delay_level < 8 then return 10
elseif self.delay_level < 9 then return 8
elseif self.delay_level < 13 then return 6
elseif self.delay_level < 15 then return 5
else return 4 end
end
function Marathon2020Game:getLineARE()
return self:getARE()
end
function Marathon2020Game:getDasLimit()
if self.delay_level < 1 then return 15
elseif self.delay_level < 3 then return 12
elseif self.delay_level < 5 then return 9
elseif self.delay_level < 8 then return 8
elseif self.delay_level < 10 then return 7
elseif self.delay_level < 13 then return 6
elseif self.delay_level < 15 then return 5
elseif self.delay_level < 20 then return 4
else return 3 end
end
function Marathon2020Game:getLineClearDelay()
if self.delay_level < 1 then return 40
elseif self.delay_level < 3 then return 25
elseif self.delay_level < 4 then return 20
elseif self.delay_level < 5 then return 15
elseif self.delay_level < 7 then return 12
elseif self.delay_level < 9 then return 8
elseif self.delay_level < 11 then return 6
elseif self.delay_level < 14 then return 4
else return 2 end
end
function Marathon2020Game:getLockDelay()
if self.delay_level < 6 then return 30
elseif self.delay_level < 7 then return 26
elseif self.delay_level < 8 then return 22
elseif self.delay_level < 9 then return 19
elseif self.delay_level < 10 then return 17
elseif self.delay_level < 16 then return 15
elseif self.delay_level < 17 then return 13
elseif self.delay_level < 18 then return 11
elseif self.delay_level < 19 then return 10
elseif self.delay_level < 20 then return 9
else return 8 end
end
function Marathon2020Game: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
local cleared_row_levels = {1, 2, 4, 6}
function Marathon2020Game:advanceOneFrame()
if self.torikan_hit then
self.no_roll_frames = self.no_roll_frames + 1
if self.no_roll_frames > 120 then
self.completed = true
end
return false
elseif self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames > 4000 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
local cool_cutoffs = {
sp(0,45,00), sp(0,41,50), sp(0,38,50), sp(0,35,00), sp(0,32,50),
sp(0,29,20), sp(0,27,20), sp(0,24,80), sp(0,22,80), sp(0,20,60),
sp(0,19,60), sp(0,19,40), sp(0,19,40), sp(0,18,40), sp(0,18,20),
sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20),
sp(0,15,20)
}
local levels_for_cleared_rows = { 1, 2, 4, 6 }
function Marathon2020Game:onPieceEnter()
self:updateLevel(1, false)
end
function Marathon2020Game:whilePieceActive()
self.grade_point_decay_counter = self.grade_point_decay_counter + self.grade + 2
if self.grade_point_decay_counter > 240 then
self.grade_point_decay_counter = 0
self.grade_points = math.max(0, self.grade_points - 1)
end
end
function Marathon2020Game:onLineClear(cleared_row_count)
self:updateLevel(levels_for_cleared_rows[cleared_row_count], true)
self:updateGrade(cleared_row_count)
end
function Marathon2020Game:updateLevel(increment, line_clear)
local new_level
if self.torikan_passed[900] == false then
if line_clear == false and (
math.floor((self.level + increment) / 100) > math.floor(self.level / 100) or
self.level == 998
) then
new_level = math.min(998, self.level + (99 - self.level % 100))
else
new_level = math.min(999, self.level + increment)
end
elseif self.torikan_passed[1900] == false then
if line_clear == false and (
math.floor((self.level + increment) / 100) > math.floor(self.level / 100) or
self.level == 1999
) then
new_level = math.min(1999, self.level + (99 - self.level % 100))
else
new_level = math.min(2000, self.level + increment)
end
else
if line_clear == false and (
self.level < 1900 and
math.floor((self.level + increment) / 100) > math.floor(self.level / 100)
) then
new_level = self.level + (99 - self.level % 100)
elseif line_clear == false and self.level + increment > 2019 then
new_level = 2019
else
new_level = math.min(2020, self.level + increment)
end
end
if not self.clear then
self:updateSectionTimes(self.level, new_level)
if not self.clear then
self.level = new_level
end
end
end
local low_cleared_line_points = {10, 20, 30, 40}
local mid_cleared_line_points = {2, 6, 12, 24}
local high_cleared_line_points = {1, 4, 9, 20}
local function getGradeForGradePoints(points)
return math.floor(math.sqrt((points / 50) * 8 + 1) / 2 - 0.5)
-- Don't be afraid of the above function. All it does is make it so that
-- you need 50 points to get to grade 1, 100 points to grade 2, etc.
end
function Marathon2020Game:updateGrade(cleared_lines)
-- update grade points and max grade points
local point_level = math.floor(self.level / 100) + self.delay_level
local plus_points = math.max(
low_cleared_line_points[cleared_lines],
mid_cleared_line_points[cleared_lines] * (1 + point_level / 2),
high_cleared_line_points[cleared_lines] * (point_level - 2),
(self.level >= 1000 and cleared_lines == 4) and self.grade * 30 or 0
)
self.grade_points = self.grade_points + plus_points
if self.grade_points > self.max_grade_points then
self.max_grade_points = self.grade_points
end
self.grade = getGradeForGradePoints(self.max_grade_points)
end
function Marathon2020Game:getTotalGrade()
return self.grade + self.section_cool_count
end
local function getSectionForLevel(level)
if level < 2001 then
return math.floor(level / 100) + 1
else
return 20
end
end
function Marathon2020Game:getEndOfSectionForSection(section)
if self.torikan_passed[900] == false and section == 10 then return 999
elseif self.torikan_passed[1900] == false and section == 20 then return 2000
elseif section == 20 then return 2020
else return section * 100 end
end
function Marathon2020Game:sectionPassed(old_level, new_level)
if self.torikan_passed[900] == false then
return (
(math.floor(old_level / 100) < math.floor(new_level / 100)) or
(new_level >= 999)
)
elseif self.torikan_passed[1900] == false then
return (
(math.floor(old_level / 100) < math.floor(new_level / 100)) or
(new_level >= 2000)
)
else
return (
(new_level < 2001 and math.floor(old_level / 100) < math.floor(new_level / 100)) or
(new_level >= 2020)
)
end
end
function Marathon2020Game:checkTorikan(section)
if section == 5 and self.frames < sp(6,00,00) then self.torikan_passed[500] = true end
if section == 9 and self.frames < sp(8,30,00) then self.torikan_passed[900] = true end
if section == 10 and self.frames < sp(8,45,00) then self.torikan_passed[1000] = true end
if section == 15 and self.frames < sp(11,30,00) then self.torikan_passed[1500] = true end
if section == 19 and self.frames < sp(13,15,00) then self.torikan_passed[1900] = true end
end
function Marathon2020Game:checkClear(level)
if (
self.torikan_passed[500] == false and level >= 500 or
self.torikan_passed[900] == false and level >= 999 or
self.torikan_passed[1000] == false and level >= 1000 or
self.torikan_passed[1500] == false and level >= 1500 or
self.torikan_passed[1900] == false and level >= 2000 or
level >= 2020
) then
if self.torikan_passed[500] == false then self.level = 500
elseif self.torikan_passed[900] == false then self.level = 999
elseif self.torikan_passed[1000] == false then self.level = 1000
elseif self.torikan_passed[1500] == false then self.level = 1500
elseif self.torikan_passed[1900] == false then self.level = 2000
else self.level = 2020 end
self.clear = true
self.grid:clear()
if (
self.torikan_passed[900] == false and level >= 999 or
level >= 2020
) then
self.roll_frames = -150
else
self.torikan_hit = true
self.no_roll_frames = -150
end
end
end
function Marathon2020Game:updateSectionTimes(old_level, new_level)
function sectionCool()
self.section_cool_count = self.section_cool_count + 1
self.delay_level = math.min(20, self.delay_level + 1)
table.insert(self.section_status, "cool")
end
local section = getSectionForLevel(old_level)
if section <= 19 and old_level % 100 < 70 and new_level >= math.floor(old_level / 100) * 100 + 70 then
-- record section 70 time
section_70_time = self.frames - self.section_start_time
table.insert(self.secondary_section_times, section_70_time)
end
if self:sectionPassed(old_level, new_level) then
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
if section > 4 then self.delay_level = math.min(20, self.delay_level + 1) end
self:checkTorikan(section)
self:checkClear(new_level)
if (
section <= 19 and self.section_status[section - 1] == "cool" and
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
self.secondary_section_times[section] < cool_cutoffs[section]
) then
sectionCool()
elseif self.section_status[section - 1] == "cool" then
table.insert(self.section_status, "none")
elseif section <= 19 and self.secondary_section_times[section] < cool_cutoffs[section] then
sectionCool()
else
table.insert(self.section_status, "none")
end
end
end
function Marathon2020Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
Marathon2020Game.mRollOpacityFunction = function(age)
if age > 300 then return 0
elseif age < 240 then return 1
else return (300 - age) / 60 end
end
Marathon2020Game.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function Marathon2020Game:qualifiesForMRoll()
return false -- until I actually have grading working
end
function Marathon2020Game:drawGrid()
if self.clear and not (self.completed or self.game_over) then
if self:qualifiesForMRoll() then
self.grid:drawInvisible(self.mRollOpacityFunction)
else
self.grid:drawInvisible(self.rollOpacityFunction)
end
else
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
end
function Marathon2020Game:drawScoringInfo()
Marathon2020Game.super.drawScoringInfo(self)
local current_section = getSectionForLevel(self.level)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("GRADE", text_x, 100, 40, "left")
love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
self:drawSectionTimesWithSecondary(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
love.graphics.printf(self.grade_points, 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(self:getEndOfSectionForSection(current_section), text_x, 370, 50, "right")
end
end
function Marathon2020Game:getHighscoreData()
return {
grade = self.grade,
level = self.level,
frames = self.frames,
}
end
function Marathon2020Game:getBackground()
return math.min(19, math.floor(self.level / 100))
end
return Marathon2020Game

View File

@@ -0,0 +1,226 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'
local MarathonA1Game = GameMode:extend()
MarathonA1Game.name = "Marathon A1"
MarathonA1Game.hash = "MarathonA1"
MarathonA1Game.tagline = "Can you score enough points to reach the title of Grand Master?"
MarathonA1Game.arr = 1
MarathonA1Game.drop_speed = 1
function MarathonA1Game:new()
MarathonA1Game.super:new()
self.roll_frames = 0
self.combo = 1
self.gm_conditions = {
level300 = false,
level500 = false,
level999 = false
}
self.randomizer = History4RollsRandomizer()
self.lock_drop = false
self.enable_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
end
function MarathonA1Game:getARE()
return 25
end
function MarathonA1Game:getLineARE()
return 25
end
function MarathonA1Game:getDasLimit()
return 15
end
function MarathonA1Game:getLineClearDelay()
return 40
end
function MarathonA1Game:getLockDelay()
return 30
end
local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400}
elseif score < 800 then return {rank = "8", next = 800}
elseif score < 1400 then return {rank = "7", next = 1400}
elseif score < 2000 then return {rank = "6", next = 2000}
elseif score < 3500 then return {rank = "5", next = 3500}
elseif score < 5500 then return {rank = "4", next = 5500}
elseif score < 8000 then return {rank = "3", next = 8000}
elseif score < 12000 then return {rank = "2", next = 12000}
elseif score < 16000 then return {rank = "1", next = 16000}
elseif score < 22000 then return {rank = "S1", next = 22000}
elseif score < 30000 then return {rank = "S2", next = 30000}
elseif score < 40000 then return {rank = "S3", next = 40000}
elseif score < 52000 then return {rank = "S4", next = 52000}
elseif score < 66000 then return {rank = "S5", next = 66000}
elseif score < 82000 then return {rank = "S6", next = 82000}
elseif score < 100000 then return {rank = "S7", next = 100000}
elseif score < 120000 then return {rank = "S8", next = 120000}
else return {rank = "S9", next = "???"}
end
end
function MarathonA1Game:getGravity()
local level = self.level
if (level < 30) then return 4/256
elseif (level < 35) then return 6/256
elseif (level < 40) then return 8/256
elseif (level < 50) then return 10/256
elseif (level < 60) then return 12/256
elseif (level < 70) then return 16/256
elseif (level < 80) then return 32/256
elseif (level < 90) then return 48/256
elseif (level < 100) then return 64/256
elseif (level < 120) then return 80/256
elseif (level < 140) then return 96/256
elseif (level < 160) then return 112/256
elseif (level < 170) then return 128/256
elseif (level < 200) then return 144/256
elseif (level < 220) then return 4/256
elseif (level < 230) then return 32/256
elseif (level < 233) then return 64/256
elseif (level < 236) then return 96/256
elseif (level < 239) then return 128/256
elseif (level < 243) then return 160/256
elseif (level < 247) then return 192/256
elseif (level < 251) then return 224/256
elseif (level < 300) then return 1
elseif (level < 330) then return 2
elseif (level < 360) then return 3
elseif (level < 400) then return 4
elseif (level < 420) then return 5
elseif (level < 450) then return 4
elseif (level < 500) then return 3
else return 20
end
end
function MarathonA1Game: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
end
return true
end
function MarathonA1Game: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 MarathonA1Game:onLineClear(cleared_row_count)
self:checkGMRequirements(self.level, self.level + cleared_row_count)
if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 then
self.clear = true
else
self.level = new_level
end
end
end
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * self.combo
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + (cleared_lines - 1) * 2
else
self.drop_bonus = 0
self.combo = 1
end
end
function MarathonA1Game:checkGMRequirements(old_level, new_level)
if old_level < 300 and new_level >= 300 then
if self.score > 12000 and self.frames <= sp(4,15) then
self.gm_conditions["level300"] = true
end
elseif old_level < 500 and new_level >= 500 then
if self.score > 40000 and self.frames <= sp(7,30) then
self.gm_conditions["level500"] = true
end
elseif old_level < 999 and new_level >= 999 then
if self.score > 126000 and self.frames <= sp(13,30) then
self.gm_conditions["level900"] = true
end
end
end
function MarathonA1Game:drawGrid()
self.grid:draw()
end
function MarathonA1Game:drawScoringInfo()
MarathonA1Game.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 .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("GRADE", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left")
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
love.graphics.printf("GM", 240, 140, 90, "left")
else
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
end
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonA1Game:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function MarathonA1Game:getBackground()
return math.floor(self.level / 100)
end
function MarathonA1Game:getHighscoreData()
return {
grade = self.grade,
score = self.score,
level = self.level,
frames = self.frames,
}
end
return MarathonA1Game

View File

@@ -0,0 +1,363 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local MarathonA2Game = GameMode:extend()
MarathonA2Game.name = "Marathon A2"
MarathonA2Game.hash = "MarathonA2"
MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible roll?"
MarathonA2Game.arr = 1
MarathonA2Game.drop_speed = 1
function MarathonA2Game:new()
MarathonA2Game.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.grade = 0
self.grade_points = 0
self.grade_point_decay_counter = 0
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
self.randomizer = History6RollsRandomizer()
self.lock_drop = false
self.enable_hold = false
self.next_queue_length = 1
end
function MarathonA2Game:getARE()
if self.level < 700 then return 25
elseif self.level < 800 then return 16
else return 12 end
end
function MarathonA2Game:getLineARE()
if self.level < 600 then return 25
elseif self.level < 700 then return 16
elseif self.level < 800 then return 12
else return 6 end
end
function MarathonA2Game:getDasLimit()
if self.level < 500 then return 15
elseif self.level < 900 then return 9
else return 7 end
end
function MarathonA2Game: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
else return 6 end
end
function MarathonA2Game:getLockDelay()
if self.level < 900 then return 30
else return 17 end
end
function MarathonA2Game: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 MarathonA2Game:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 3694 then
self.completed = true
if self.grade == 32 then
self.grade = 33
end
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function MarathonA2Game: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 MarathonA2Game:onLineClear(cleared_row_count)
self:updateSectionTimes(self.level, self.level + 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
if self:qualifiesForMRoll() then
self.grade = 32
end
self.grid:clear()
self.roll_frames = -150
end
end
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
self:updateGrade(cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
function MarathonA2Game:updateSectionTimes(old_level, new_level)
if self.clear then return end
if math.floor(old_level / 100) < math.floor(new_level / 100) or
new_level >= 999 then
-- record new section
section_time = self.frames - self.section_start_time
self.section_times[math.floor(old_level / 100)] = section_time
self.section_start_time = self.frames
end
end
local grade_point_bonuses = {
{10, 20, 40, 50},
{10, 20, 30, 40},
{10, 20, 30, 40},
{10, 15, 30, 40},
{10, 15, 20, 40},
{5, 15, 20, 30},
{5, 10, 20, 30},
{5, 10, 15, 30},
{5, 10, 15, 30},
{5, 10, 15, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
}
local grade_point_decays = {
125, 80, 80, 50, 45, 45, 45,
40, 40, 40, 40, 40, 30, 30, 30,
20, 20, 20, 20, 20,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
10, 10
}
local combo_multipliers = {
{1.0, 1.0, 1.0, 1.0},
{1.2, 1.4, 1.5, 1.0},
{1.2, 1.5, 1.8, 1.0},
{1.4, 1.6, 2.0, 1.0},
{1.4, 1.7, 2.2, 1.0},
{1.4, 1.8, 2.3, 1.0},
{1.4, 1.9, 2.4, 1.0},
{1.5, 2.0, 2.5, 1.0},
{1.5, 2.1, 2.6, 1.0},
{2.0, 2.5, 3.0, 1.0},
}
local grade_conversion = {
[0] = 0,
1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
7, 8, 8, 8, 9, 9, 9, 10, 11, 12,
12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17, 18, 19
}
function MarathonA2Game:updateGrade(cleared_lines)
if self.clear then return end
if cleared_lines == 0 then
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
self.grade_point_decay_counter = 0
self.grade_points = math.max(0, self.grade_points - 1)
end
else
self.grade_points = self.grade_points + (
math.ceil(
grade_point_bonuses[self.grade + 1][cleared_lines] *
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
) * (1 + math.floor(self.level / 250))
)
if self.grade_points >= 100 and self.grade < 31 then
self.grade_points = 0
self.grade = self.grade + 1
end
end
end
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
function MarathonA2Game:qualifiesForMRoll()
if not self.clear then return false end
-- tetris requirements
for section = 0, 9 do
if self.section_tetrises[section] < tetris_requirements[section] then
return false
end
end
-- section time requirements
local section_average = 0
for section = 0, 4 do
section_average = section_average + self.section_times[section]
if self.section_times[section] > sp(1,05) then
return false
end
end
-- section time average requirements
if self.section_times[5] > section_average / 5 then
return false
end
for section = 6, 9 do
if self.section_times[section] > self.section_times[section - 1] + 120 then
return false
end
end
if self.grade < 17 or self.frames > sp(8,45) then
return false
end
return true
end
function MarathonA2Game:getLetterGrade()
local grade = grade_conversion[self.grade]
if grade < 9 then
return tostring(9 - grade)
elseif grade < 18 then
return "S" .. tostring(grade - 8)
elseif grade == 18 then
return "M"
else
return "GM"
end
end
MarathonA2Game.rollOpacityFunction = function(age)
if age < 240 then return 1
elseif age > 300 then return 0
else return 1 - (age - 240) / 60 end
end
MarathonA2Game.mRollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function MarathonA2Game:drawGrid(ruleset)
if self.clear and not (self.completed or self.game_over) then
if self:qualifiesForMRoll() then
self.grid:drawInvisible(self.mRollOpacityFunction)
else
self.grid:drawInvisible(self.rollOpacityFunction)
end
else
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
end
function MarathonA2Game:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("GRADE", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
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")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonA2Game:getHighscoreData()
return {
grade = grade_conversion[self.grade],
score = self.score,
level = self.level,
frames = self.frames,
}
end
function MarathonA2Game:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function MarathonA2Game:getBackground()
return math.floor(self.level / 100)
end
return MarathonA2Game

View File

@@ -0,0 +1,431 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local MarathonA3Game = GameMode:extend()
MarathonA3Game.name = "Marathon A3"
MarathonA3Game.hash = "MarathonA3"
MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all the Section COOLs?"
MarathonA3Game.arr = 1
MarathonA3Game.drop_speed = 1
function MarathonA3Game:new()
MarathonA3Game.super:new()
self.speed_level = 0
self.roll_frames = 0
self.combo = 1
self.grade = 0
self.grade_points = 0
self.roll_points = 0
self.grade_point_decay_counter = 0
self.section_cool_grade = 0
self.section_status = { [0] = "none" }
self.section_start_time = 0
self.section_70_times = { [0] = 0 }
self.section_times = { [0] = 0 }
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function MarathonA3Game:getARE()
if self.speed_level < 700 then return 27
elseif self.speed_level < 800 then return 18
elseif self.speed_level < 1000 then return 14
elseif self.speed_level < 1100 then return 8
elseif self.speed_level < 1200 then return 7
else return 6 end
end
function MarathonA3Game:getLineARE()
if self.speed_level < 600 then return 27
elseif self.speed_level < 700 then return 18
elseif self.speed_level < 800 then return 14
elseif self.speed_level < 1100 then return 8
elseif self.speed_level < 1200 then return 7
else return 6 end
end
function MarathonA3Game:getDasLimit()
if self.speed_level < 500 then return 15
elseif self.speed_level < 900 then return 9
else return 7 end
end
function MarathonA3Game:getLineClearDelay()
if self.speed_level < 500 then return 40
elseif self.speed_level < 600 then return 25
elseif self.speed_level < 700 then return 16
elseif self.speed_level < 800 then return 12
else return 6 end
end
function MarathonA3Game:getLockDelay()
if self.speed_level < 900 then return 30
elseif self.speed_level < 1100 then return 17
else return 15 end
end
function MarathonA3Game:getGravity()
if (self.speed_level < 30) then return 4/256
elseif (self.speed_level < 35) then return 6/256
elseif (self.speed_level < 40) then return 8/256
elseif (self.speed_level < 50) then return 10/256
elseif (self.speed_level < 60) then return 12/256
elseif (self.speed_level < 70) then return 16/256
elseif (self.speed_level < 80) then return 32/256
elseif (self.speed_level < 90) then return 48/256
elseif (self.speed_level < 100) then return 64/256
elseif (self.speed_level < 120) then return 80/256
elseif (self.speed_level < 140) then return 96/256
elseif (self.speed_level < 160) then return 112/256
elseif (self.speed_level < 170) then return 128/256
elseif (self.speed_level < 200) then return 144/256
elseif (self.speed_level < 220) then return 4/256
elseif (self.speed_level < 230) then return 32/256
elseif (self.speed_level < 233) then return 64/256
elseif (self.speed_level < 236) then return 96/256
elseif (self.speed_level < 239) then return 128/256
elseif (self.speed_level < 243) then return 160/256
elseif (self.speed_level < 247) then return 192/256
elseif (self.speed_level < 251) then return 224/256
elseif (self.speed_level < 300) then return 1
elseif (self.speed_level < 330) then return 2
elseif (self.speed_level < 360) then return 3
elseif (self.speed_level < 400) then return 4
elseif (self.speed_level < 420) then return 5
elseif (self.speed_level < 450) then return 4
elseif (self.speed_level < 500) then return 3
else return 20
end
end
function MarathonA3Game: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")
end
return
elseif self.roll_frames > 3238 then
if self:qualifiesForMRoll() then
self.roll_points = self.roll_points + 160
else
self.roll_points = self.roll_points + 50
end
switchBGM(nil)
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function MarathonA3Game:onPieceEnter()
if (self.level % 100 ~= 99) and self.level ~= 998 and self.frames ~= 0 then
self:updateSectionTimes(self.level, self.level + 1)
self.level = self.level + 1
self.speed_level = self.speed_level + 1
end
end
local cleared_row_levels = {1, 2, 4, 6}
function MarathonA3Game:onLineClear(cleared_row_count)
local advanced_levels = cleared_row_levels[cleared_row_count]
self:updateSectionTimes(self.level, self.level + advanced_levels)
if not self.clear then
self.level = math.min(self.level + advanced_levels, 999)
end
self.speed_level = self.speed_level + advanced_levels
if self.level == 999 and not self.clear then
self.clear = true
self.grid:clear()
self.roll_frames = -150
end
end
local cool_cutoffs = {
sp(0,52), sp(0,52), sp(0,49), sp(0,45), sp(0,45),
sp(0,42), sp(0,42), sp(0,38), sp(0,38),
}
local regret_cutoffs = {
sp(0,90), sp(0,75), sp(0,75), sp(0,68), sp(0,60),
sp(0,60), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
}
function MarathonA3Game:updateSectionTimes(old_level, new_level)
if self.clear then return end
local section = math.floor(old_level / 100) + 1
if math.floor(old_level / 100) < math.floor(new_level / 100) or
new_level >= 999 then
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
if section_time > regret_cutoffs[section] then
self.section_cool_grade = self.section_cool_grade - 1
table.insert(self.section_status, "regret")
elseif section <= 9 and self.section_status[section - 1] == "cool" and
self.section_70_times[section] < self.section_70_times[section - 1] + 1000 then
self.section_cool_grade = self.section_cool_grade + 1
self.speed_level = self.speed_level + 100
table.insert(self.section_status, "cool")
elseif self.section_status[section - 1] == "cool" then
table.insert(self.section_status, "none")
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
self.section_cool_grade = self.section_cool_grade + 1
self.speed_level = self.speed_level + 100
table.insert(self.section_status, "cool")
else
table.insert(self.section_status, "none")
end
elseif section <= 9 and old_level % 100 < 70 and new_level % 100 >= 70 then
-- record section 70 time
section_70_time = self.frames - self.section_start_time
table.insert(self.section_70_times, section_70_time)
end
end
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
self:updateGrade(cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
local grade_point_bonuses = {
{10, 20, 40, 50},
{10, 20, 30, 40},
{10, 20, 30, 40},
{10, 15, 30, 40},
{10, 15, 20, 40},
{5, 15, 20, 30},
{5, 10, 20, 30},
{5, 10, 15, 30},
{5, 10, 15, 30},
{5, 10, 15, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
{2, 12, 13, 30},
}
local grade_point_decays = {
125, 80, 80, 50, 45, 45, 45,
40, 40, 40, 40, 40, 30, 30, 30,
20, 20, 20, 20, 20,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
10, 10
}
local combo_multipliers = {
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.2, 1.4, 1.5},
{1.0, 1.2, 1.5, 1.8},
{1.0, 1.4, 1.6, 2.0},
{1.0, 1.4, 1.7, 2.2},
{1.0, 1.4, 1.8, 2.3},
{1.0, 1.4, 1.9, 2.4},
{1.0, 1.5, 2.0, 2.5},
{1.0, 1.5, 2.1, 2.6},
{1.0, 2.0, 2.5, 3.0},
}
local roll_points = {4, 8, 12, 26}
local mroll_points = {10, 20, 30, 100}
local grade_conversion = {
[0] = 0,
1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
7, 8, 8, 8, 9, 9, 9, 10, 11, 12, 12,
12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17
}
function MarathonA3Game:updateGrade(cleared_lines)
if cleared_lines == 0 then
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
self.grade_point_decay_counter = 0
self.grade_points = math.max(0, self.grade_points - 1)
end
else
if self.clear then
if self:qualifiesForMRoll() then
self.roll_points = self.roll_points + mroll_points[cleared_lines]
else
self.roll_points = self.roll_points + roll_points[cleared_lines]
end
else
self.grade_points = self.grade_points + (
math.ceil(
grade_point_bonuses[self.grade + 1][cleared_lines] *
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
) * (1 + math.floor(self.level / 250))
)
if self.grade_points >= 100 and self.grade < 31 then
self.grade_points = 0
self.grade = self.grade + 1
end
end
end
end
function MarathonA3Game:qualifiesForMRoll()
return self.grade >= 27 and self.section_cool_grade >= 9
end
function MarathonA3Game:getAggregateGrade()
return self.section_cool_grade + math.floor(self.roll_points / 100) + grade_conversion[self.grade]
end
local master_grades = { "M", "MK", "MV", "MO", "MM" }
function MarathonA3Game:getLetterGrade()
local grade = self:getAggregateGrade()
if grade < 9 then
return tostring(9 - grade)
elseif grade < 18 then
return "S" .. tostring(grade - 8)
elseif grade < 27 then
return "M" .. tostring(grade - 17)
elseif grade < 32 then
return master_grades[grade - 26]
else
return "GM"
end
end
function MarathonA3Game:drawGrid()
if self.clear and not (self.completed or self.game_over) then
if self:qualifiesForMRoll() then
self.grid:drawInvisible(self.mRollOpacityFunction)
else
self.grid:drawInvisible(self.rollOpacityFunction)
end
else
self.grid:draw()
end
end
MarathonA3Game.rollOpacityFunction = function(age)
if age < 240 then return 1
elseif age > 300 then return 0
else return 1 - (age - 240) / 60 end
end
MarathonA3Game.mRollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function MarathonA3Game:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("GRADE", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
-- draw section time data
current_section = math.floor(self.level / 100) + 1
section_x = 530
section_70_x = 440
for section, time in pairs(self.section_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
end
end
for section, time in pairs(self.section_70_times) do
if section > 0 then
love.graphics.printf(formatTime(time), section_70_x, 40 + 20 * section, 90, "left")
end
end
local current_x
if table.getn(self.section_times) < table.getn(self.section_70_times) then
current_x = section_x
else
current_x = section_70_x
end
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
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(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonA3Game:getHighscoreData()
return {
grade = self:getAggregateGrade(),
level = self.level,
frames = self.frames,
}
end
function MarathonA3Game:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function MarathonA3Game:getBackground()
return math.floor(self.level / 100)
end
return MarathonA3Game

View File

@@ -0,0 +1,176 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local MarathonL1Game = GameMode:extend()
MarathonL1Game.name = "Line Attack"
MarathonL1Game.hash = "MarathonL1"
MarathonL1Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
MarathonL1Game.arr = 1
MarathonL1Game.drop_speed = 1
function MarathonL1Game:new()
MarathonL1Game.super:new()
self.roll_frames = 0
self.randomizer = History6RollsRandomizer()
self.section_time_limit = 3600
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.section_clear = false
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function MarathonL1Game:getARE()
if self.lines < 10 then return 18
elseif self.lines < 40 then return 14
elseif self.lines < 60 then return 12
elseif self.lines < 70 then return 10
elseif self.lines < 80 then return 8
elseif self.lines < 90 then return 7
else return 6 end
end
function MarathonL1Game:getLineARE()
return self:getARE()
end
function MarathonL1Game:getDasLimit()
if self.lines < 20 then return 10
elseif self.lines < 50 then return 9
elseif self.lines < 70 then return 8
else return 7 end
end
function MarathonL1Game:getLineClearDelay()
if self.lines < 10 then return 14
elseif self.lines < 30 then return 9
else return 5 end
end
function MarathonL1Game:getLockDelay()
if self.lines < 10 then return 28
elseif self.lines < 20 then return 24
elseif self.lines < 30 then return 22
elseif self.lines < 40 then return 20
elseif self.lines < 50 then return 18
elseif self.lines < 70 then return 14
else return 13 end
end
function MarathonL1Game:getGravity()
return 20
end
function MarathonL1Game:getSection()
return math.floor(level / 100) + 1
end
function MarathonL1Game:advanceOneFrame()
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
return true
end
function MarathonL1Game: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, 150)
if self.lines == 150 then
self.clear = true
self.roll_frames = -150
end
end
end
function MarathonL1Game:getSectionTime()
return self.frames - self.section_start_time
end
function MarathonL1Game:updateSectionTimes(old_lines, new_lines)
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
-- record new section
table.insert(self.section_times, self:getSectionTime())
self.section_start_time = self.frames
self.section_clear = true
end
end
function MarathonL1Game:onPieceEnter()
self.section_clear = false
end
function MarathonL1Game:drawGrid(ruleset)
self.grid:draw()
end
function MarathonL1Game:getHighscoreData()
return {
lines = self.lines,
frames = self.frames,
}
end
function MarathonL1Game:drawScoringInfo()
MarathonL1Game.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 .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
love.graphics.printf("LINES", 240, 320, 40, "left")
local current_section = math.floor(self.lines / 10) + 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 < sp(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
love.graphics.setColor(1, 1, 1, 1)
end
function MarathonL1Game:getSectionEndLines()
return math.floor(self.lines / 10 + 1) * 10
end
function MarathonL1Game:getBackground()
return math.floor(self.lines / 10)
end
return MarathonL1Game

170
tetris/modes/pacer_test.lua Normal file
View File

@@ -0,0 +1,170 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local PacerTest = GameMode:extend()
PacerTest.name = "TetrisGram™ Pacer Test"
PacerTest.hash = "PacerTest"
PacerTest.tagline = ""
PacerTest.arr = 1
PacerTest.drop_speed = 1
local function getLevelFrames(level)
if level == 1 then return 72 * 60 / 8.0
else return 72 * 60 / (8 + level * 0.5)
end
end
local level_end_sections = {
7, 15, 23, 32, 41, 51, 61, 72, 83, 94,
106, 118, 131, 144, 157, 171, 185, 200,
215, 231, 247
}
function PacerTest:new()
PacerTest.super:new()
self.ready_frames = 2430
self.clear_frames = 0
self.randomizer = History6RollsRandomizer()
self.level = 1
self.section = 0
self.level_frames = 0
self.section_lines = 0
self.section_clear = false
self.strikes = 0
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.instant_hard_drop = true
self.instant_soft_drop = false
self.next_queue_length = 3
end
function PacerTest:initialize(ruleset)
for i = 1, 30 do
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
self.level_frames = getLevelFrames(1)
switchBGM("pacer_test")
end
function PacerTest:getARE()
return 0
end
function PacerTest:getLineARE()
return 0
end
function PacerTest:getDasLimit()
return 8
end
function PacerTest:getLineClearDelay()
return 6
end
function PacerTest:getLockDelay()
return 30
end
function PacerTest:getGravity()
return 1/64
end
function PacerTest:getSection()
return math.floor(level / 100) + 1
end
function PacerTest:advanceOneFrame()
if self.clear then
self.clear_frames = self.clear_frames + 1
if self.clear_frames > 600 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
self.level_frames = self.level_frames - 1
if self.level_frames <= 0 then
self:checkSectionStatus()
self.section = self.section + 1
if self.section >= level_end_sections[self.level] then
self.level = self.level + 1
end
self.level_frames = self.level_frames + getLevelFrames(self.level)
end
end
return true
end
function PacerTest:checkSectionStatus()
if self.section_clear then
self.strikes = 0
self.section_clear = false
else
self.strikes = self.strikes + 1
if self.strikes >= 200 then
self.game_over = true
fadeoutBGM(2.5)
end
end
self.section_lines = 0
end
function PacerTest:onLineClear(cleared_row_count)
self.section_lines = self.section_lines + cleared_row_count
if self.section_lines >= 3 then
self.section_clear = true
end
end
function PacerTest:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function PacerTest:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function PacerTest:drawScoringInfo()
PacerTest.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, 224, 70, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
for i = 1, math.min(self.strikes, 3) do
love.graphics.draw(misc_graphics["strike"], text_x + (i - 1) * 30, 280)
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.section_lines .. "/3", text_x, 244, 40, "left")
love.graphics.printf(self.level, text_x, 340, 40, "right")
love.graphics.printf(self.section, text_x, 370, 40, "right")
end
function PacerTest:getBackground()
return self.level - 1
end
return PacerTest

View File

@@ -0,0 +1,199 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local PhantomManiaGame = GameMode:extend()
PhantomManiaGame.name = "Phantom Mania"
PhantomManiaGame.hash = "PhantomMania"
PhantomManiaGame.tagline = "The blocks disappear as soon as they're locked! Can you remember where everything is?"
PhantomManiaGame.arr = 1
PhantomManiaGame.drop_speed = 1
function PhantomManiaGame:new()
PhantomManiaGame.super:new()
self.lock_drop = true
self.next_queue_length = 1
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
end
function PhantomManiaGame:getARE()
if self.level < 100 then return 18
elseif self.level < 200 then return 14
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function PhantomManiaGame:getLineARE()
if self.level < 100 then return 18
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function PhantomManiaGame:getDasLimit()
if self.level < 200 then return 11
elseif self.level < 300 then return 10
elseif self.level < 400 then return 9
else return 7 end
end
function PhantomManiaGame:getLineClearDelay()
return self:getLineARE()
end
function PhantomManiaGame: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 PhantomManiaGame:getGravity()
return 20
end
function PhantomManiaGame:hitTorikan(old_level, new_level)
if old_level < 300 and new_level >= 300 and self.frames > sp(2,28) then
self.level = 300
return true
end
if old_level < 500 and new_level >= 500 and self.frames > sp(3,38) then
self.level = 500
return true
end
if old_level < 800 and new_level >= 800 and self.frames > sp(5,23) then
self.level = 800
return true
end
return false
end
function PhantomManiaGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames > 1982 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function PhantomManiaGame: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 PhantomManiaGame:onLineClear(cleared_row_count)
if not self.clear then
local new_level = self.level + cleared_row_count
if self:hitTorikan(self.level, new_level) then
if new_level >= 999 then
self.level = 999
end
self.clear = true
else
self.level = new_level
end
end
end
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
PhantomManiaGame.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function PhantomManiaGame:drawGrid()
if not (self.game_over or self.clear) then
self.grid:drawInvisible(self.rollOpacityFunction)
else
self.grid:draw()
end
end
local function getLetterGrade(level, clear)
if level < 300 or level == 300 and clear then
return ""
elseif level < 500 or level == 500 and clear then
return "M"
elseif level < 700 then
return "MK"
elseif level < 800 or level == 800 and clear then
return "MV"
elseif level < 900 then
return "MO"
elseif level < 999 then
return "MM"
elseif level == 999 then
return "GM"
end
end
function PhantomManiaGame:drawScoringInfo()
PhantomManiaGame.super.drawScoringInfo(self)
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")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 40, "right")
if self.clear then
love.graphics.printf(self.level, text_x, 370, 40, "right")
else
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
end
end
function PhantomManiaGame:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function PhantomManiaGame:getBackground()
return math.floor(self.level / 100)
end
function PhantomManiaGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return PhantomManiaGame

View File

@@ -0,0 +1,303 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local PhantomMania2Game = GameMode:extend()
PhantomMania2Game.name = "Phantom Mania 2"
PhantomMania2Game.hash = "PhantomMania2"
PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make it to level 1300?"
PhantomMania2Game.arr = 1
PhantomMania2Game.drop_speed = 1
function PhantomMania2Game:new()
PhantomMania2Game.super:new()
self.level = 0
self.grade = 0
self.garbage = 0
self.clear = false
self.completed = false
self.roll_frames = 0
self.combo = 1
self.hold_age = 0
self.queue_age = 0
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function PhantomMania2Game:getARE()
if self.level < 300 then return 12
else return 6 end
end
function PhantomMania2Game:getLineARE()
if self.level < 100 then return 8
elseif self.level < 200 then return 7
elseif self.level < 500 then return 6
elseif self.level < 1300 then return 5
else return 6 end
end
function PhantomMania2Game:getDasLimit()
if self.level < 200 then return 9
elseif self.level < 500 then return 7
else return 5 end
end
function PhantomMania2Game:getLineClearDelay()
return self:getLineARE() - 2
end
function PhantomMania2Game:getLockDelay()
if self.level < 200 then return 18
elseif self.level < 300 then return 17
elseif self.level < 500 then return 15
elseif self.level < 600 then return 13
else return 12 end
end
function PhantomMania2Game:getGravity()
return 20
end
function PhantomMania2Game:getGarbageLimit()
if self.level < 600 then return 20
elseif self.level < 700 then return 18
elseif self.level < 800 then return 10
elseif self.level < 900 then return 9
else return 8 end
end
function PhantomMania2Game:getNextPiece(ruleset)
return {
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function PhantomMania2Game:hitTorikan(old_level, new_level)
if old_level < 300 and new_level >= 300 and self.frames > sp(2,02) then
self.level = 300
return true
end
if old_level < 500 and new_level >= 500 and self.frames > sp(3,03) then
self.level = 500
return true
end
if old_level < 800 and new_level >= 800 and self.frames > sp(4,40) then
self.level = 800
return true
end
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,38) then
self.level = 1000
return true
end
return false
end
function PhantomMania2Game: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)
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
self.hold_age = self.hold_age + 1
end
return true
end
function PhantomMania2Game:whilePieceActive()
self.queue_age = self.queue_age + 1
end
function PhantomMania2Game:onPieceEnter()
self.queue_age = 0
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
local cleared_row_levels = {1, 2, 4, 6}
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
function PhantomMania2Game: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
if new_level >= 1300 then
self.level = 1300
end
self.clear = true
self.grid:clear()
self.roll_frames = -150
else
self.level = math.min(new_level, 1300)
end
self:advanceBottomRow(-cleared_row_count)
end
end
function PhantomMania2Game:onPieceLock()
self:advanceBottomRow(1)
end
function PhantomMania2Game:onHold()
self.hold_age = 0
end
function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
local cool_cutoffs = {
sp(0,36), sp(0,36), sp(0,36), sp(0,36), sp(0,36),
sp(0,30), sp(0,30), sp(0,30), sp(0,30), sp(0,30),
sp(0,27), sp(0,27), sp(0,27),
}
local regret_cutoffs = {
sp(0,50), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
sp(0,40), sp(0,40), sp(0,40), sp(0,40), sp(0,40),
sp(0,35), sp(0,35), sp(0,35),
}
function PhantomMania2Game: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 <= cool_cutoffs[section] then
self.grade = self.grade + 2
elseif section_time <= regret_cutoffs[section] then
self.grade = self.grade + 1
end
end
end
function PhantomMania2Game:advanceBottomRow(dx)
if self.level >= 500 and self.level < 1000 then
self.garbage = math.max(self.garbage + dx, 0)
if self.garbage >= self:getGarbageLimit() then
self.grid:copyBottomRow()
self.garbage = 0
end
end
end
PhantomMania2Game.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
PhantomMania2Game.garbageOpacityFunction = function(age)
if age > 30 then return 0
else return 1 - age / 30 end
end
function PhantomMania2Game:drawGrid()
if not (self.game_over or self.clear) then
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
else
self.grid:draw()
end
end
local function getLetterGrade(grade)
if grade == 0 then
return "1"
elseif grade <= 9 then
return "S" .. tostring(grade)
else
return "M" .. tostring(grade - 9)
end
end
function PhantomMania2Game:setNextOpacity(i)
if self.level > 1000 then
local hidden_next_pieces = math.floor(self.level / 100) - 10
if i < hidden_next_pieces then
love.graphics.setColor(1, 1, 1, 0)
elseif i == hidden_next_pieces then
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.queue_age / 4))
else
love.graphics.setColor(1, 1, 1, 1)
end
else
love.graphics.setColor(1, 1, 1, 1)
end
end
function PhantomMania2Game:setHoldOpacity()
if self.level > 1000 then
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.hold_age / 15))
else
love.graphics.setColor(1, 1, 1, 1)
end
end
function PhantomMania2Game:drawScoringInfo()
PhantomMania2Game.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")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(math.floor(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 PhantomMania2Game:getBackground()
return math.floor(self.level / 100)
end
function PhantomMania2Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
grade = self.grade,
}
end
return PhantomMania2Game

View File

@@ -0,0 +1,200 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local PhantomManiaGame = GameMode:extend()
PhantomManiaGame.name = "Phantom Mania N"
PhantomManiaGame.hash = "PhantomManiaN"
PhantomManiaGame.tagline = "The old mode from Nullpomino."
PhantomManiaGame.arr = 1
PhantomManiaGame.drop_speed = 1
function PhantomManiaGame:new()
PhantomManiaGame.super:new()
self.lock_drop = true
self.next_queue_length = 3
self.enable_hold = true
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
end
function PhantomManiaGame:getARE()
if self.level < 100 then return 18
elseif self.level < 200 then return 14
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function PhantomManiaGame:getLineARE()
if self.level < 100 then return 18
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function PhantomManiaGame:getDasLimit()
if self.level < 200 then return 11
elseif self.level < 300 then return 10
elseif self.level < 400 then return 9
else return 7 end
end
function PhantomManiaGame:getLineClearDelay()
return self:getLineARE()
end
function PhantomManiaGame: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 PhantomManiaGame:getGravity()
return 20
end
function PhantomManiaGame:hitTorikan(old_level, new_level)
if old_level < 300 and new_level >= 300 and self.frames > sp(2,28) then
self.level = 300
return true
end
if old_level < 500 and new_level >= 500 and self.frames > sp(3,38) then
self.level = 500
return true
end
if old_level < 800 and new_level >= 800 and self.frames > sp(5,23) then
self.level = 800
return true
end
return false
end
function PhantomManiaGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames > 1982 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function PhantomManiaGame: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 PhantomManiaGame:onLineClear(cleared_row_count)
if not self.clear then
local new_level = self.level + cleared_row_count
if self:hitTorikan(self.level, new_level) then
if new_level >= 999 then
self.level = 999
end
self.clear = true
else
self.level = new_level
end
end
end
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
PhantomManiaGame.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function PhantomManiaGame:drawGrid()
if not (self.game_over or self.clear) then
self.grid:drawInvisible(self.rollOpacityFunction)
else
self.grid:draw()
end
end
local function getLetterGrade(level, clear)
if level < 300 or level == 300 and clear then
return ""
elseif level < 500 or level == 500 and clear then
return "M"
elseif level < 700 then
return "MK"
elseif level < 800 or level == 800 and clear then
return "MV"
elseif level < 900 then
return "MO"
elseif level < 999 then
return "MM"
elseif level == 999 then
return "GM"
end
end
function PhantomManiaGame:drawScoringInfo()
PhantomManiaGame.super.drawScoringInfo(self)
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")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 40, "right")
if self.clear then
love.graphics.printf(self.level, text_x, 370, 40, "right")
else
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
end
end
function PhantomManiaGame:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function PhantomManiaGame:getBackground()
return math.floor(self.level / 100)
end
function PhantomManiaGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return PhantomManiaGame

157
tetris/modes/strategy.lua Normal file
View File

@@ -0,0 +1,157 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local StrategyGame = GameMode:extend()
StrategyGame.name = "Strategy"
StrategyGame.hash = "Strategy"
StrategyGame.tagline = "You have lots of time to think! Can you use it to place a piece fast?"
StrategyGame.arr = 1
StrategyGame.drop_speed = 1
function StrategyGame:new()
StrategyGame.super:new()
self.level = 0
self.clear = false
self.completed = false
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.enable_hold = false
self.next_queue_length = 1
end
function StrategyGame:getARE()
if self.level < 100 then return 60
elseif self.level < 200 then return 54
elseif self.level < 300 then return 48
elseif self.level < 400 then return 42
elseif self.level < 500 then return 36
elseif self.level < 600 then return 30
elseif self.level < 700 then return 24
elseif self.level < 800 then return 21
elseif self.level < 900 then return 18
else return 15 end
end
function StrategyGame:getLineARE()
return self:getARE()
end
function StrategyGame:getDasLimit()
return 6
end
function StrategyGame:getLineClearDelay()
return self:getARE()
end
function StrategyGame:getLockDelay()
if self.level < 500 then return 8
elseif self.level < 700 then return 6
else return 4 end
end
function StrategyGame:getGravity()
return 20
end
function StrategyGame:getNextPiece(ruleset)
return {
skin = "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function StrategyGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 1936 then
switchBGM(nil)
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function StrategyGame:onPieceEnter()
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function StrategyGame:onLineClear(cleared_row_count)
if not self.clear then
self.level = math.min(999, self.level + cleared_row_count)
if self.level == 999 then
self.clear = true
end
end
end
function StrategyGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
function StrategyGame:setNextOpacity(i)
if self.are == 0 then love.graphics.setColor(1, 1, 1, 1)
else love.graphics.setColor(1, 1, 1, self.are / self:getARE())
end
end
function StrategyGame:drawGrid()
self.grid:draw()
end
function StrategyGame:drawScoringInfo()
StrategyGame.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("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
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 StrategyGame:getBackground()
return math.floor(self.level / 100)
end
function StrategyGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return StrategyGame

View File

@@ -0,0 +1,265 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local Survival2020Game = GameMode:extend()
Survival2020Game.name = "Survival 2020"
Survival2020Game.hash = "Survival2020"
Survival2020Game.tagline = "A new time limit on the blocks! Can you handle being forced to perform under the total delay?"
Survival2020Game.arr = 1
Survival2020Game.drop_speed = 1
function Survival2020Game:new()
Survival2020Game.super:new()
self.level = 0
self.grade = 0
self.garbage = 0
self.clear = false
self.completed = false
self.roll_frames = 0
self.combo = 1
self.total_delay = 0
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function Survival2020Game:getARE()
if self.level < 200 then return 12
elseif self.level < 300 then return 10
elseif self.level < 500 then return 6
elseif self.level < 1000 then return 4
elseif self.level < 1500 then return 3
else return 2 end
end
function Survival2020Game:getLineARE()
return self:getARE()
end
function Survival2020Game:getDasLimit()
if self.level < 200 then return 9
elseif self.level < 500 then return 7
elseif self.level < 1000 then return 5
elseif self.level < 1500 then return 4
else return 3 end
end
function Survival2020Game:getLineClearDelay()
if self.level < 300 then return 6
elseif self.level < 500 then return 4
else return 2 end
end
function Survival2020Game:getLockDelay()
if self.level < 100 then return 20
elseif self.level < 200 then return 18
elseif self.level < 300 then return 17
elseif self.level < 400 then return 15
elseif self.level < 500 then return 14
elseif self.level < 1000 then return 13
elseif self.level < 1500 then return 10
else return 8 end
end
function Survival2020Game:getTotalDelay()
if self.level < 500 then return 60
elseif self.level < 600 then return 45 -- lock delay: 15
elseif self.level < 700 then return 36
elseif self.level < 800 then return 27
elseif self.level < 900 then return 21
elseif self.level < 1000 then return 15
elseif self.level < 1100 then return 36 -- lock delay: 11
elseif self.level < 1200 then return 27
elseif self.level < 1300 then return 21
elseif self.level < 1400 then return 15
elseif self.level < 1500 then return 12
elseif self.level < 1600 then return 30 -- lock delay: 8
elseif self.level < 1700 then return 21
elseif self.level < 1800 then return 15
elseif self.level < 1900 then return 12
elseif self.level < 2020 then return 10
else return 30 end
end
function Survival2020Game:getGravity()
return 20
end
function Survival2020Game:getNextPiece(ruleset)
return {
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function Survival2020Game:hitTorikan(old_level, new_level)
if old_level < 500 and new_level >= 500 and self.frames > sp(3,00) then
self.level = 500
return true
end
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,00) then
self.level = 1000
return true
end
if old_level < 1500 and new_level >= 1500 and self.frames > sp(7,00) then
self.level = 1500
return true
end
return false
end
function Survival2020Game: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)
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
if self.piece ~= nil then
self.total_delay = self.total_delay + 1
if self.total_delay >= self:getTotalDelay() then
self.piece:dropToBottom(self.grid)
self.piece.locked = true
end
end
end
return true
end
function Survival2020Game:onPieceEnter()
if not self.clear and (
(self.level < 1900 and self.level % 100 ~= 99) or
self.level == 2019
) then
self.level = self.level + 1
end
self.total_delay = 0
end
local cleared_row_levels = {1, 2, 4, 6}
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
function Survival2020Game: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 >= 2020 or self:hitTorikan(self.level, new_level) then
if new_level >= 2020 then
self.level = 2020
end
self.clear = true
self.grid:clear()
self.roll_frames = -150
else
self.level = math.min(new_level, 2020)
end
end
end
function Survival2020Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
function Survival2020Game: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 <= sp(0,30) then
self.grade = self.grade + 2
else
self.grade = self.grade + 1
end
end
end
Survival2020Game.opacityFunction = function(age)
if age > 300 then return 0
else return 1 - Math.max(age - 240, 0) / 60 end
end
function Survival2020Game:drawGrid()
if self.level < 1500 then
self.grid:draw()
else
self.grid:drawInvisible(self.opacityFunction)
end
end
local function getLetterGrade(grade)
if grade == 0 then
return "1"
elseif grade <= 9 then
return "S" .. tostring(grade)
else
return "M" .. tostring(grade - 9)
end
end
function Survival2020Game:drawScoringInfo()
Survival2020Game.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")
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(math.floor(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 Survival2020Game:getBackground()
return math.floor(self.level / 100)
end
function Survival2020Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
grade = self.grade,
}
end
return Survival2020Game

View File

@@ -0,0 +1,198 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'
local SurvivalA1Game = GameMode:extend()
SurvivalA1Game.name = "Survival A1"
SurvivalA1Game.hash = "SurvivalA1"
SurvivalA1Game.tagline = "The game starts fast and only gets faster!"
SurvivalA1Game.arr = 1
SurvivalA1Game.drop_speed = 1
function SurvivalA1Game:new()
SurvivalA1Game.super:new()
self.roll_frames = 0
self.combo = 1
self.gm_conditions = {
level300 = false,
level500 = false,
level999 = false
}
self.randomizer = History4RollsRandomizer()
self.lock_drop = false
self.enable_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
end
function SurvivalA1Game:getARE()
return 25
end
function SurvivalA1Game:getLineARE()
return 25
end
function SurvivalA1Game:getDasLimit()
return 15
end
function SurvivalA1Game:getLineClearDelay()
return 40
end
function SurvivalA1Game:getLockDelay()
return 30
end
function SurvivalA1Game:getGravity()
return 20
end
local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400}
elseif score < 800 then return {rank = "8", next = 800}
elseif score < 1400 then return {rank = "7", next = 1400}
elseif score < 2000 then return {rank = "6", next = 2000}
elseif score < 3500 then return {rank = "5", next = 3500}
elseif score < 5500 then return {rank = "4", next = 5500}
elseif score < 8000 then return {rank = "3", next = 8000}
elseif score < 12000 then return {rank = "2", next = 12000}
elseif score < 16000 then return {rank = "1", next = 16000}
elseif score < 22000 then return {rank = "S1", next = 22000}
elseif score < 30000 then return {rank = "S2", next = 30000}
elseif score < 40000 then return {rank = "S3", next = 40000}
elseif score < 52000 then return {rank = "S4", next = 52000}
elseif score < 66000 then return {rank = "S5", next = 66000}
elseif score < 82000 then return {rank = "S6", next = 82000}
elseif score < 100000 then return {rank = "S7", next = 100000}
elseif score < 120000 then return {rank = "S8", next = 120000}
else return {rank = "S9", next = "???"}
end
end
function SurvivalA1Game: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
end
return true
end
function SurvivalA1Game: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 SurvivalA1Game:onLineClear(cleared_row_count)
self:checkGMRequirements(self.level, self.level + cleared_row_count)
if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 then
self.clear = true
else
self.level = new_level
end
end
end
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * self.combo
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + (cleared_lines - 1) * 2
else
self.drop_bonus = 0
self.combo = 1
end
end
function SurvivalA1Game:checkGMRequirements(old_level, new_level)
if old_level < 300 and new_level >= 300 then
if self.score > 12000 and self.frames <= sp(4,15) then
self.gm_conditions["level300"] = true
end
elseif old_level < 500 and new_level >= 500 then
if self.score > 40000 and self.frames <= sp(7,30) then
self.gm_conditions["level500"] = true
end
elseif old_level < 999 and new_level >= 999 then
if self.score > 126000 and self.frames <= sp(13,30) then
self.gm_conditions["level900"] = true
end
end
end
function SurvivalA1Game:drawGrid()
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
function SurvivalA1Game:drawScoringInfo()
SurvivalA1Game.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 .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("GRADE", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left")
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
love.graphics.printf("GM", 240, 140, 90, "left")
else
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
end
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function SurvivalA1Game:getSectionEndLevel()
if self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function SurvivalA1Game:getBackground()
return math.floor(self.level / 100)
end
function SurvivalA1Game:getHighscoreData()
return {
grade = self.grade,
score = self.score,
level = self.level,
frames = self.frames,
}
end
return SurvivalA1Game

View File

@@ -0,0 +1,166 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local SurvivalA2Game = GameMode:extend()
SurvivalA2Game.name = "Survival A2"
SurvivalA2Game.hash = "SurvivalA2"
SurvivalA2Game.tagline = "The game starts fast and only gets faster!"
SurvivalA2Game.arr = 1
SurvivalA2Game.drop_speed = 1
function SurvivalA2Game:new()
SurvivalA2Game.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
end
function SurvivalA2Game:getARE()
if self.level < 100 then return 18
elseif self.level < 300 then return 14
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function SurvivalA2Game:getLineARE()
if self.level < 100 then return 14
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end
end
function SurvivalA2Game:getDasLimit()
if self.level < 200 then return 11
elseif self.level < 300 then return 10
elseif self.level < 400 then return 9
else return 7 end
end
function SurvivalA2Game:getLineClearDelay()
return self:getLineARE()
end
function SurvivalA2Game: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 SurvivalA2Game:getGravity()
return 20
end
function SurvivalA2Game:hitTorikan(old_level, new_level)
if old_level < 500 and new_level >= 500 and self.frames > sp(3,25) then
self.level = 500
return true
end
return false
end
function SurvivalA2Game: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
end
return true
end
function SurvivalA2Game:onPieceEnter()
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function SurvivalA2Game:onLineClear(cleared_row_count)
if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 or self:hitTorikan(self.level, new_level) then
self.clear = true
else
self.level = new_level
end
end
end
function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * self.combo
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + (cleared_lines - 1) * 2
else
self.drop_bonus = 0
self.combo = 1
end
end
function SurvivalA2Game:getLetterGrade()
if self.level >= 999 then return "GM"
elseif self.level >= 500 then return "M"
else return "" end
end
function SurvivalA2Game:drawGrid()
self.grid:draw()
end
function SurvivalA2Game:drawScoringInfo()
SurvivalA2Game.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.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
st(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
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")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left")
love.graphics.printf(self.level, text_x, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
end
function SurvivalA2Game:getSectionEndLevel()
if self.clear then return self.level
elseif self.level > 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function SurvivalA2Game:getBackground()
return math.floor(self.level / 100)
end
function SurvivalA2Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
return SurvivalA2Game

View File

@@ -0,0 +1,236 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SurvivalA3Game = GameMode:extend()
SurvivalA3Game.name = "Survival A3"
SurvivalA3Game.hash = "SurvivalA3"
SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?"
SurvivalA3Game.arr = 1
SurvivalA3Game.drop_speed = 1
function SurvivalA3Game:new()
SurvivalA3Game.super:new()
self.level = 0
self.grade = 0
self.garbage = 0
self.clear = false
self.completed = false
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function SurvivalA3Game:getARE()
if self.level < 300 then return 12
else return 6 end
end
function SurvivalA3Game:getLineARE()
if self.level < 100 then return 8
elseif self.level < 200 then return 7
elseif self.level < 500 then return 6
elseif self.level < 1300 then return 5
else return 6 end
end
function SurvivalA3Game:getDasLimit()
if self.level < 200 then return 9
elseif self.level < 500 then return 7
else return 5 end
end
function SurvivalA3Game:getLineClearDelay()
return self:getLineARE() - 2
end
function SurvivalA3Game:getLockDelay()
if self.level < 200 then return 18
elseif self.level < 300 then return 17
elseif self.level < 500 then return 15
elseif self.level < 600 then return 13
elseif self.level < 1100 then return 12
elseif self.level < 1200 then return 10
else return 8 end
end
function SurvivalA3Game:getGravity()
return 20
end
function SurvivalA3Game:getGarbageLimit()
if self.level < 600 then return 20
elseif self.level < 700 then return 18
elseif self.level < 800 then return 10
elseif self.level < 900 then return 9
else return 8 end
end
function SurvivalA3Game:getNextPiece(ruleset)
return {
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function SurvivalA3Game:hitTorikan(old_level, new_level)
if old_level < 500 and new_level >= 500 and self.frames > sp(2,28) then
self.level = 500
return true
end
if old_level < 1000 and new_level >= 1000 and self.frames > sp(4,56) then
self.level = 1000
return true
end
return false
end
function SurvivalA3Game: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)
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function SurvivalA3Game:onPieceEnter()
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
local cleared_row_levels = {1, 2, 4, 6}
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
function SurvivalA3Game: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
if new_level >= 1300 then
self.level = 1300
end
self.clear = true
self.grid:clear()
self.roll_frames = -150
else
self.level = math.min(new_level, 1300)
end
self:advanceBottomRow(-cleared_row_count)
end
end
function SurvivalA3Game:onPieceLock()
self:advanceBottomRow(1)
end
function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
function SurvivalA3Game: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 <= sp(1,00) then
self.grade = self.grade + 1
end
end
end
function SurvivalA3Game:advanceBottomRow(dx)
if self.level >= 500 and self.level < 1000 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 SurvivalA3Game:drawGrid()
self.grid:draw()
end
local function getLetterGrade(grade)
if grade == 0 then
return "1"
elseif grade <= 9 then
return "S" .. tostring(grade)
else
return "M" .. tostring(grade - 9)
end
end
function SurvivalA3Game:drawScoringInfo()
SurvivalA3Game.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")
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(math.floor(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 SurvivalA3Game:getBackground()
return math.floor(self.level / 100)
end
function SurvivalA3Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
grade = self.grade,
}
end
return SurvivalA3Game