Renamed Marathon AX4 to Survival AX, among other things.

pull/16/head
Joe Zeng 2020-12-03 00:26:36 -05:00 committed by Joe Z
parent 0f3883e18d
commit 79a25c3954
11 changed files with 285 additions and 73 deletions

View File

@ -3,19 +3,11 @@ Game modes
There are several classes of game modes. The modes that originate from other games are organized by suffix: There are several classes of game modes. The modes that originate from other games are organized by suffix:
* The "C" series stand for "Classic" games, games that were produced before around 1992-1993 and generally have no wallkicks or lock delay.
* C84 - The original version from the Electronika 60.
* C88 - Sega Tetris.
* C89 - Nintendo / NES Tetris.
* The "A" series stand for "Arika" games, or games in the Tetris the Grand Master series. * The "A" series stand for "Arika" games, or games in the Tetris the Grand Master series.
* A1 - Tetris The Grand Master (the original from 1998). * A1 - Tetris The Grand Master (the original from 1998).
* A2 - Tetris The Absolute The Grand Master 2 PLUS. * A2 - Tetris The Absolute The Grand Master 2 PLUS.
* A3 - Tetris The Grand Master 3 Terror-Instinct. * A3 - Tetris The Grand Master 3 Terror-Instinct.
* AX - Tetris The Grand Master ACE (X for Xbox). * AX - Tetris The Grand Master ACE (X for Xbox).
* The "G" series stand for "Guideline" games, or games that follow the Tetris Guideline.
* GF - Tetris Friends (2007-2019)
* GJ - Tetris Online Japan (2005-2011)
* N stands for Nullpomino, only used for Phantom Mania N.
MARATHON MARATHON
-------- --------
@ -28,8 +20,6 @@ From other games:
* **MARATHON A1**: Tetris the Grand Master 1. * **MARATHON A1**: Tetris the Grand Master 1.
* **MARATHON A2**: Tetris the Grand Master 2 (TAP Master). * **MARATHON A2**: Tetris the Grand Master 2 (TAP Master).
* **MARATHON A3**: Tetris the Grand Master 3 (no exams). * **MARATHON A3**: Tetris the Grand Master 3 (no exams).
* **MARATHON AX4**: Another mode from TGM Ace.
* **MARATHON C89**: Nintendo NES Tetris. Can you transition and make it to the killscreen?
SURVIVAL SURVIVAL
@ -43,14 +33,7 @@ From other games:
* **SURVIVAL A1**: 20G mode from Tetris the Grand Master. * **SURVIVAL A1**: 20G mode from Tetris the Grand Master.
* **SURVIVAL A2**: T.A. Death. * **SURVIVAL A2**: T.A. Death.
* **SURVIVAL A3**: Ti Shirase. * **SURVIVAL A3**: Ti Shirase.
* **SURVIVAL AX**: Another mode from TGM Ace.
RACE
----
Modes with no levels, just a single timed goal.
* **Race 40**: How fast can you clear 40 lines? No limits, no holds barred.
PHANTOM MANIA PHANTOM MANIA
@ -69,8 +52,4 @@ OTHER MODES
* **Strategy**: How well can you plan ahead your movements? Can you handle only having a short time to place each piece? * **Strategy**: How well can you plan ahead your movements? Can you handle only having a short time to place each piece?
* **TetrisGram™ Pacer Test**: is a multi-stage piece-placing ability test that progressively gets more difficult as it continues. * **Big A2**: Marathon A2 but all the pieces are BIG!
* **Interval Training**: 30 seconds per section. 20G. 15 frames of lock delay. How long can you last?
* **Demon Mode**: An original mode from Oshisaure! Can you push through the ever faster levels and not get denied?

View File

@ -5,15 +5,15 @@ local Piece = require 'tetris.components.piece'
local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart' local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart'
local MarathonAX4Game = GameMode:extend() local SurvivalAXGame = GameMode:extend()
MarathonAX4Game.name = "Marathon AX4" SurvivalAXGame.name = "Survival AX"
MarathonAX4Game.hash = "MarathonAX4" SurvivalAXGame.hash = "SurvivalAX"
MarathonAX4Game.tagline = "Can you clear the time hurdles when the game goes this fast?" SurvivalAXGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
function MarathonAX4Game:new() function SurvivalAXGame:new()
MarathonAX4Game.super:new() SurvivalAXGame.super:new()
self.roll_frames = 0 self.roll_frames = 0
self.randomizer = Bag7NoSZOStartRandomizer() self.randomizer = Bag7NoSZOStartRandomizer()
@ -29,7 +29,7 @@ function MarathonAX4Game:new()
self.next_queue_length = 3 self.next_queue_length = 3
end end
function MarathonAX4Game:getARE() function SurvivalAXGame:getARE()
if self.lines < 10 then return 18 if self.lines < 10 then return 18
elseif self.lines < 40 then return 14 elseif self.lines < 40 then return 14
elseif self.lines < 60 then return 12 elseif self.lines < 60 then return 12
@ -39,24 +39,24 @@ function MarathonAX4Game:getARE()
else return 6 end else return 6 end
end end
function MarathonAX4Game:getLineARE() function SurvivalAXGame:getLineARE()
return self:getARE() return self:getARE()
end end
function MarathonAX4Game:getDasLimit() function SurvivalAXGame:getDasLimit()
if self.lines < 20 then return 10 if self.lines < 20 then return 10
elseif self.lines < 50 then return 9 elseif self.lines < 50 then return 9
elseif self.lines < 70 then return 8 elseif self.lines < 70 then return 8
else return 7 end else return 7 end
end end
function MarathonAX4Game:getLineClearDelay() function SurvivalAXGame:getLineClearDelay()
if self.lines < 10 then return 14 if self.lines < 10 then return 14
elseif self.lines < 30 then return 9 elseif self.lines < 30 then return 9
else return 5 end else return 5 end
end end
function MarathonAX4Game:getLockDelay() function SurvivalAXGame:getLockDelay()
if self.lines < 10 then return 28 if self.lines < 10 then return 28
elseif self.lines < 20 then return 24 elseif self.lines < 20 then return 24
elseif self.lines < 30 then return 22 elseif self.lines < 30 then return 22
@ -66,15 +66,15 @@ function MarathonAX4Game:getLockDelay()
else return 13 end else return 13 end
end end
function MarathonAX4Game:getGravity() function SurvivalAXGame:getGravity()
return 20 return 20
end end
function MarathonAX4Game:getSection() function SurvivalAXGame:getSection()
return math.floor(level / 100) + 1 return math.floor(level / 100) + 1
end end
function MarathonAX4Game:advanceOneFrame() function SurvivalAXGame:advanceOneFrame()
if self.clear then if self.clear then
self.roll_frames = self.roll_frames + 1 self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then if self.roll_frames < 0 then
@ -93,7 +93,7 @@ function MarathonAX4Game:advanceOneFrame()
return true return true
end end
function MarathonAX4Game:onLineClear(cleared_row_count) function SurvivalAXGame:onLineClear(cleared_row_count)
if not self.clear then if not self.clear then
local new_lines = self.lines + cleared_row_count local new_lines = self.lines + cleared_row_count
self:updateSectionTimes(self.lines, new_lines) self:updateSectionTimes(self.lines, new_lines)
@ -106,11 +106,11 @@ function MarathonAX4Game:onLineClear(cleared_row_count)
end end
end end
function MarathonAX4Game:getSectionTime() function SurvivalAXGame:getSectionTime()
return self.frames - self.section_start_time return self.frames - self.section_start_time
end end
function MarathonAX4Game:updateSectionTimes(old_lines, new_lines) function SurvivalAXGame:updateSectionTimes(old_lines, new_lines)
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
-- record new section -- record new section
table.insert(self.section_times, self:getSectionTime()) table.insert(self.section_times, self:getSectionTime())
@ -119,23 +119,23 @@ function MarathonAX4Game:updateSectionTimes(old_lines, new_lines)
end end
end end
function MarathonAX4Game:onPieceEnter() function SurvivalAXGame:onPieceEnter()
self.section_clear = false self.section_clear = false
end end
function MarathonAX4Game:drawGrid(ruleset) function SurvivalAXGame:drawGrid(ruleset)
self.grid:draw() self.grid:draw()
end end
function MarathonAX4Game:getHighscoreData() function SurvivalAXGame:getHighscoreData()
return { return {
lines = self.lines, lines = self.lines,
frames = self.frames, frames = self.frames,
} }
end end
function MarathonAX4Game:drawScoringInfo() function SurvivalAXGame:drawScoringInfo()
MarathonAX4Game.super.drawScoringInfo(self) SurvivalAXGame.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@ -165,12 +165,12 @@ function MarathonAX4Game:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
end end
function MarathonAX4Game:getSectionEndLines() function SurvivalAXGame:getSectionEndLines()
return math.floor(self.lines / 10 + 1) * 10 return math.floor(self.lines / 10 + 1) * 10
end end
function MarathonAX4Game:getBackground() function SurvivalAXGame:getBackground()
return math.floor(self.lines / 10) return math.floor(self.lines / 10)
end end
return MarathonAX4Game return SurvivalAXGame

View File

@ -110,13 +110,7 @@ function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function ARS:get180RotationValue() function ARS:get180RotationValue() return 3 end
if config.gamesettings.world_reverse == 3 then
return 1
else
return 3
end
end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default

View File

@ -49,4 +49,8 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -36,4 +36,8 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -4,7 +4,19 @@ local Ruleset = require 'tetris.rulesets.ti_srs'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
SRS.name = "ACE-SRS" SRS.name = "ACE-SRS"
SRS.hash = "ACE Standard" SRS.hash = "StandardACE"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.MANIPULATIONS_MAX = 128 SRS.MANIPULATIONS_MAX = 128

View File

@ -99,4 +99,8 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -364,9 +364,9 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial)
if rot_dir == 0 then return end if rot_dir == 0 then return end
if self.world and config.gamesettings.world_reverse == 2 then if config.gamesettings.world_reverse == 3 or (self.world and config.gamesettings.world_reverse == 2) then
rot_dir = 4 - rot_dir rot_dir = 4 - rot_dir
end end
local new_piece = piece:withRelativeRotation(rot_dir) local new_piece = piece:withRelativeRotation(rot_dir)
self:attemptWallkicks(piece, new_piece, rot_dir, grid) self:attemptWallkicks(piece, new_piece, rot_dir, grid)

View File

@ -0,0 +1,206 @@
local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.ruleset'
local SRS = Ruleset:extend()
SRS.name = "Guideline SRS"
SRS.hash = "Standard"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true
SRS.spawn_positions = {
I = { x=5, y=2 },
J = { x=4, y=3 },
L = { x=4, y=3 },
O = { x=5, y=3 },
S = { x=4, y=3 },
T = { x=4, y=3 },
Z = { x=4, y=3 },
}
SRS.big_spawn_positions = {
I = { x=3, y=0 },
J = { x=2, y=1 },
L = { x=2, y=1 },
O = { x=3, y=1 },
S = { x=2, y=1 },
T = { x=2, y=1 },
Z = { x=2, y=1 },
}
SRS.block_offsets = {
I={
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
},
J={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
},
L={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
},
O={
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
},
S={
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
},
T={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
},
Z={
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
}
}
SRS.wallkicks_3x3 = {
[0]={
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[2]={{x=0, y=1}, {x=0, y=-1}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[1]={
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[3]={{x=0, y=1}, {x=0, y=-1}},
},
[2]={
[0]={{x=0, y=1}, {x=0, y=-1}},
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[3]={
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
[1]={{x=0, y=1}, {x=0, y=-1}},
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
},
};
SRS.wallkicks_line = {
[0]={
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
[2]={},
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
},
[1]={
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
},
[2]={
[0]={},
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
},
[3]={
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
},
};
function SRS:check_new_low(piece)
for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y
if y > piece.lowest_y then
piece.manipulations = 0
piece.lowest_y = y
end
end
end
-- Component functions.
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
local kicks
if piece.shape == "O" then
return
elseif piece.shape == "I" then
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
else
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
end
assert(piece.rotation ~= new_piece.rotation)
for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return
end
end
end
function SRS:onPieceCreate(piece, grid)
piece.manipulations = 0
piece.lowest_y = -math.huge
end
function SRS:onPieceDrop(piece, grid)
self:check_new_low(piece)
if piece.manipulations >= 15 and piece:isDropBlocked(grid) then
piece.locked = true
else
piece.lock_delay = 0 -- step reset
end
end
function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 15 then
piece.locked = true
end
end
end
function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset
self:check_new_low(piece)
piece.manipulations = piece.manipulations + 1
if piece:isDropBlocked(grid) then
if piece.manipulations >= 15 then
piece.locked = true
end
end
end
return SRS

View File

@ -3,18 +3,32 @@ local Ruleset = require 'tetris.rulesets.arika_srs'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
SRS.name = "Guideline SRS" SRS.name = "SRS-X"
SRS.hash = "Standard" SRS.hash = "StandardEXP"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true SRS.enable_IRS_wallkicks = true
SRS.MANIPULATIONS_MAX = 15 SRS.MANIPULATIONS_MAX = 24
SRS.ROTATIONS_MAX = 12
function SRS:checkNewLow(piece) function SRS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y local y = piece.position.y + block.y
if y > piece.lowest_y then if y > piece.lowest_y then
piece.manipulations = 0 piece.manipulations = 0
piece.rotations = 0
piece.lowest_y = y piece.lowest_y = y
end end
end end
@ -72,6 +86,7 @@ end
function SRS:onPieceCreate(piece, grid) function SRS:onPieceCreate(piece, grid)
piece.manipulations = 0 piece.manipulations = 0
piece.rotations = 0
piece.lowest_y = -math.huge piece.lowest_y = -math.huge
end end
@ -88,7 +103,7 @@ function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= self.MANIPULATIONS_MAX then if piece.manipulations >= 24 then
piece.locked = true piece.locked = true
end end
end end
@ -97,8 +112,8 @@ end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:checkNewLow(piece) self:checkNewLow(piece)
piece.manipulations = piece.manipulations + 1 piece.rotations = piece.rotations + 1
if piece.manipulations >= self.MANIPULATIONS_MAX then if piece.rotations >= self.ROTATIONS_MAX then
piece:moveInGrid({ x = 0, y = 1 }, 1, grid) piece:moveInGrid({ x = 0, y = 1 }, 1, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true

View File

@ -4,7 +4,7 @@ local Ruleset = require 'tetris.rulesets.ruleset'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
SRS.name = "Ti-World" SRS.name = "Ti-World"
SRS.hash = "Bad I-kicks" SRS.hash = "StandardTI"
SRS.world = true SRS.world = true
SRS.colourscheme = { SRS.colourscheme = {
I = "C", I = "C",
@ -192,12 +192,6 @@ function SRS:onPieceRotate(piece, grid)
end end
end end
function SRS:get180RotationValue() function SRS:get180RotationValue() return 3 end
if config.gamesettings.world_reverse == 1 then
return 1
else
return 3
end
end
return SRS return SRS