Compare commits

..

50 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
9ac60cbb5e afterLineClear func added and splits time draw fix 2021-02-15 12:26:52 -05:00
Ishaan Bhardwaj
cdd846c3e6 Made the volume sliders scroll more consistently 2021-02-13 22:00:45 -05:00
Ishaan Bhardwaj
33d260b753 Removed the print statement from A2 2021-02-12 23:31:13 -05:00
Ishaan Bhardwaj
1644fcdf8e Bigint exponentiation by 1 now returns a clone 2021-02-12 10:05:04 -05:00
Ishaan Bhardwaj
f3c1cf6e1f Fixed an issue where DS-World wouldn't harddrop 2021-02-11 22:11:35 -05:00
Ishaan Bhardwaj
06a8a2ebf7 Mandate safelock on 0 ARE rulesets/modes 2021-02-11 22:08:52 -05:00
Ishaan Bhardwaj
15354ce004 dropToBottom no longer resets lock delay
it's already handled by the rulesets anyhow
2021-02-11 21:20:23 -05:00
Ishaan Bhardwaj
af02cd3467 Classic lock (GB/NES-like) added as a gamemode var 2021-02-11 15:46:56 -05:00
Ishaan Bhardwaj
acb05918c1 Custom line clear animations 2021-02-10 23:10:10 -05:00
Ishaan Bhardwaj
b644c8e457 Revert "Default line clear animation set to fadeout"
Please, reminder to self, TEST YOUR COMMITS.
This reverts commit 288961e12a.
2021-02-10 22:46:58 -05:00
Ishaan Bhardwaj
288961e12a Default line clear animation set to fadeout 2021-02-10 22:41:07 -05:00
Ishaan Bhardwaj
a047e51681 Framework for custom line clear animations added
Colored fadeout is the default
2021-02-10 18:35:51 -05:00
Ishaan Bhardwaj
77f24f5ee5 Human readable bigint output changes 2021-02-10 12:45:55 -05:00
Ishaan Bhardwaj
32c2274bef Optimized bigint exponentiation (again) 2021-02-10 11:38:10 -05:00
Ishaan Bhardwaj
4920e5de1c Added another type check to the bigint 2021-02-10 11:15:56 -05:00
Ishaan Bhardwaj
8418fc8ab7 Update README.md 2021-02-10 10:32:18 -05:00
Ishaan Bhardwaj
711a5120f1 Update README.md 2021-02-10 10:31:52 -05:00
Ishaan Bhardwaj
e7c3c9446a Cambridge banner looks better on dark theme now
Courtesy of @sinefuse
2021-02-10 09:05:10 -05:00
Ishaan Bhardwaj
3ac39acd7a Removed bigint comparison metamethods (read below)
Use bigint.compare from now on
2021-02-09 12:27:57 -05:00
Ishaan Bhardwaj
d0505251b3 Spawn positions now ruleset dependent
Is configurable in options
2021-02-08 23:23:50 -05:00
Ishaan Bhardwaj
bb0fe2ac20 BigInt now has a digits method (read comments)
Kind of unnecessary but included for completeness
2021-02-08 16:56:06 -05:00
Ishaan Bhardwaj
986ebac47f BigInt division fixed 2021-02-08 16:07:48 -05:00
Ishaan Bhardwaj
9799147f96 Revert "BigInt fixes and optimization (read comments)"
Apparently division *still* isn't being handled correctly.
This reverts commit 1dda12e4be.
2021-02-08 14:53:19 -05:00
Ishaan Bhardwaj
1dda12e4be BigInt fixes and optimization (read comments)
Fixed a nasty division bug where intermediate operations could result in negative zero. Optimized exponentiation to use exponentiation by squaring.
2021-02-08 14:10:34 -05:00
Ishaan Bhardwaj
38947e00c0 Added a tostring function for bigints 2021-02-08 10:34:47 -05:00
Ishaan Bhardwaj
035f6dd7b4 Fixed big division when (big1 < big2) 2021-02-08 10:23:10 -05:00
Ishaan Bhardwaj
aa3eadc93d Update README.md 2021-02-08 09:00:51 -05:00
Ishaan Bhardwaj
cb6962825f Update package.bat script 2021-02-07 20:50:27 -05:00
Ishaan Bhardwaj
b5e7ce5be6 Grid outline draw refactorization 2021-02-05 22:13:10 -05:00
Ishaan Bhardwaj
1ccd6a09d3 Gamemodes have a default (empty) name 2021-02-05 21:44:29 -05:00
Ishaan Bhardwaj
5a074f77cf Adjusted how DAS cut subtracts from the counter 2021-02-03 16:50:03 -05:00
Ishaan Bhardwaj
81677221f1 Fixed 0 next queue modes 2021-02-03 11:42:21 -05:00
Ishaan Bhardwaj
a998be6f7b Global vars suck. Nothing more 2021-02-02 22:30:28 -05:00
Ishaan Bhardwaj
9c1c8eea21 Added default high score retrieval method 2021-02-02 14:51:49 -05:00
Ishaan Bhardwaj
f022c6c4b7 Sakura no longer draws game over effect on completion 2021-02-01 15:58:30 -05:00
Ishaan Bhardwaj
38f3d23b95 More default methods for gamemodes provided 2021-02-01 15:41:43 -05:00
Ishaan Bhardwaj
816d27db39 Set default gravity for gamemode 2021-02-01 14:50:31 -05:00
Ishaan Bhardwaj
ce08ffd3da SRS-X fixed to use symmetric wallkicks 2021-01-30 22:28:34 -05:00
Ishaan Bhardwaj
f0e84a8874 SRS-X rotate lock reset behavior fixed 2021-01-30 16:54:09 -05:00
Ishaan Bhardwaj
5e02471fb4 SRS now has upgraded 180s 2021-01-30 16:49:52 -05:00
Oshisaure
fa2fe77081 Apparently macs don't have a printscreen key, screenshot bound to f12 now instead 2021-01-29 22:29:27 +00:00
Joe Z
682c4a485a Updated fonts. 2021-01-29 12:24:54 -05:00
Oshisaure
68760105cc Bound printscreen to saving screenshots 2021-01-29 04:13:17 +00:00
Ishaan Bhardwaj
e19da98ea1 Standard SRS now has correct amount of move resets 2021-01-28 21:19:47 -05:00
Ishaan Bhardwaj
e8904b92ed check_new_low doesn't exist! 2021-01-28 21:15:04 -05:00
Ishaan Bhardwaj
4f574e7716 Guideline SRS now specifies dependency 2021-01-28 21:13:31 -05:00
Ishaan Bhardwaj
f1528e8d71 Fixed the SRS variants from latest commit. 2021-01-28 21:05:36 -05:00
Joe Zeng
79a25c3954 Renamed Marathon AX4 to Survival AX, among other things. 2021-01-28 01:15:21 -05:00
Ishaan Bhardwaj
0f3883e18d Sakura ghost piece fix 2021-01-27 18:28:12 -05:00
Ishaan Bhardwaj
1acd0ec65a Holding a piece that would block you out now works 2021-01-27 13:29:53 -05:00
31 changed files with 501 additions and 301 deletions

View File

@@ -1,11 +1,11 @@
![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png) ![Cambridge Banner](https://t-sp.in/public/img/cambridge.png)
Cambridge Cambridge
========= =========
Welcome to Cambridge, the next open-source falling-block game engine! Welcome to Cambridge, the next open-source falling-block game engine!
This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)! The project is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)!
Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4 Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4
@@ -40,7 +40,13 @@ Playing the game
You do not need LÖVE on Windows, as it comes bundled with the program. You do not need LÖVE on Windows, as it comes bundled with the program.
To get the stable release, simply download the ZIP in the latest release. All assets needed are bundled with the executable. #### Stable release
To get the stable release, simply download either `cambridge-win32.zip` (32-bit) or `cambridge-windows.zip` (64-bit) in the [latest release](https://github.com/sashlilac/cambridge/releases/latest).
All assets needed are bundled with the executable.
#### Bleeding edge
If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip). If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
@@ -82,13 +88,7 @@ It should run automatically!
## Installing modpacks ## Installing modpacks
Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically. For instructions on how to install modpacks, go to [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
You can also load custom assets through this way, assuming you preserve the directory structure.
**WARNING:** The .exe / .love files and the bleeding edge releases have different save directories. Read the above link carefully!
For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
License License
------- -------

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

@@ -85,4 +85,8 @@ function table.contains(table, element)
end end
end end
return false return false
end end
function clamp(a, b, c)
return math.min(a, math.max(b, c))
end

View File

@@ -55,15 +55,6 @@ function bigint.new(num)
__pow = function(lhs, rhs) __pow = function(lhs, rhs)
return bigint.exponentiate(lhs, rhs) return bigint.exponentiate(lhs, rhs)
end, end,
__eq = function(lhs, rhs)
return bigint.compare(lhs, rhs, "==")
end,
__lt = function(lhs, rhs)
return bigint.compare(lhs, rhs, "<")
end,
__le = function(lhs, rhs)
return bigint.compare(lhs, rhs, "<=")
end,
__tostring = function() __tostring = function()
return bigint.unserialize(self, "s") return bigint.unserialize(self, "s")
end end
@@ -91,7 +82,8 @@ function bigint.check(big, force)
assert(type(big.sign) == "string", "bigint is unsigned") assert(type(big.sign) == "string", "bigint is unsigned")
for _, digit in pairs(big.digits) do for _, digit in pairs(big.digits) do
assert(type(digit) == "number", digit .. " is not a number") assert(type(digit) == "number", digit .. " is not a number")
assert(digit < 10, digit .. " is greater than or equal to 10") assert(digit <= 9 and digit >= 0, digit .. " is not between 0 and 9")
assert(math.floor(digit) == digit, digit .. " is not an integer")
end end
end end
return true return true
@@ -106,6 +98,12 @@ function bigint.abs(big)
return result return result
end end
-- Return the number of digits in the big
function bigint.digits(big)
bigint.check(big)
return #big.digits
end
-- Convert a big to a number or string -- Convert a big to a number or string
function bigint.unserialize(big, output_type, precision) function bigint.unserialize(big, output_type, precision)
bigint.check(big) bigint.check(big)
@@ -140,7 +138,7 @@ function bigint.unserialize(big, output_type, precision)
-- Unserialization to human-readable form or scientific notation only -- Unserialization to human-readable form or scientific notation only
-- requires reading the first few digits -- requires reading the first few digits
if (precision == nil) then if (precision == nil) then
precision = 3 precision = math.min(#big.digits, 3)
else else
assert(precision > 0, "Precision cannot be less than 1") assert(precision > 0, "Precision cannot be less than 1")
assert(math.floor(precision) == precision, assert(math.floor(precision) == precision,
@@ -149,17 +147,18 @@ function bigint.unserialize(big, output_type, precision)
-- num is the first (precision + 1) digits, the first being separated by -- num is the first (precision + 1) digits, the first being separated by
-- a decimal point from the others -- a decimal point from the others
num = num .. big.digits[1] num = num .. math.floor(big.digits[1])
if (precision > 1) then if (precision > 1) then
num = num .. "." num = num .. "."
for i = 1, (precision - 1) do for i = 1, (precision - 1) do
num = num .. big.digits[i + 1] num = num .. math.floor(big.digits[i + 1])
end end
end end
if ((output_type == "human-readable") if ((output_type == "human-readable")
or (output_type == "human") or (output_type == "human")
or (output_type == "h")) then or (output_type == "h"))
and (#big.digits >= 3 and #big.digits <= 10002) then
-- Human-readable output contributed by 123eee555 -- Human-readable output contributed by 123eee555
local name local name
@@ -438,7 +437,6 @@ function bigint.multiply(big1, big2)
return result return result
end end
-- Raise a big to a positive integer or big power (TODO: negative integer power) -- Raise a big to a positive integer or big power (TODO: negative integer power)
function bigint.exponentiate(big, power) function bigint.exponentiate(big, power)
-- Type checking for big done by bigint.multiply -- Type checking for big done by bigint.multiply
@@ -449,13 +447,23 @@ function bigint.exponentiate(big, power)
if (bigint.compare(exp, bigint.new(0), "==")) then if (bigint.compare(exp, bigint.new(0), "==")) then
return bigint.new(1) return bigint.new(1)
elseif (bigint.compare(exp, bigint.new(1), "==")) then elseif (bigint.compare(exp, bigint.new(1), "==")) then
return big return big:clone()
else else
local result = big:clone() local result = bigint.new(1)
local base = big:clone()
while (bigint.compare(exp, bigint.new(1), ">")) do while (true) do
result = bigint.multiply(result, big) if (bigint.compare(
exp = bigint.subtract(exp, bigint.new(1)) bigint.modulus(exp, bigint.new(2)), bigint.new(1), "=="
)) then
result = bigint.multiply(result, base)
end
if (bigint.compare(exp, bigint.new(1), "==")) then
break
else
exp = bigint.divide(exp, bigint.new(2))
base = bigint.multiply(base, base)
end
end end
return result return result
@@ -471,7 +479,7 @@ function bigint.divide_raw(big1, big2)
if (bigint.compare(big1, big2, "==")) then if (bigint.compare(big1, big2, "==")) then
return bigint.new(1), bigint.new(0) return bigint.new(1), bigint.new(0)
elseif (bigint.compare(big1, big2, "<")) then elseif (bigint.compare(big1, big2, "<")) then
return bigint.new(0), bigint.new(0) return bigint.new(0), big1:clone()
else else
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero") assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
assert(big1.sign == "+", "error: big1 is not positive") assert(big1.sign == "+", "error: big1 is not positive")
@@ -479,54 +487,35 @@ function bigint.divide_raw(big1, big2)
local result = bigint.new() local result = bigint.new()
local dividend = bigint.new() -- Dividend of a single operation, not the local dividend = bigint.new() -- Dividend of a single operation
-- dividend of the overall function
local divisor = big2:clone()
local factor = 1
-- Walk left to right among digits in the dividend, like in long local neg_zero = bigint.new(0)
-- division neg_zero.sign = "-"
for _, digit in pairs(big1.digits) do
dividend.digits[#dividend.digits + 1] = digit
-- The dividend is smaller than the divisor, so a zero is appended for i = 1, #big1.digits do
-- to the result and the loop ends -- Fixes a negative zero bug
if (bigint.compare(dividend, divisor, "<")) then if (#dividend.digits ~= 0) and (bigint.compare(dividend, neg_zero, "==")) then
if (#result.digits > 0) then -- Don't add leading zeroes dividend = bigint.new()
result.digits[#result.digits + 1] = 0
end
else
-- Find the maximum number of divisors that fit into the
-- dividend
factor = 0
while (bigint.compare(divisor, dividend, "<=")) do
divisor = bigint.add(divisor, big2)
factor = factor + 1
end end
-- Append the factor to the result table.insert(dividend.digits, big1.digits[i])
if (factor == 10) then
-- Fixes a weird bug that introduces a new bug if fixed by local factor = bigint.new(0)
-- changing the comparison in the while loop to "<=" while bigint.compare(dividend, big2, ">=") do
result.digits[#result.digits] = 1 dividend = bigint.subtract(dividend, big2)
result.digits[#result.digits + 1] = 0 factor = bigint.add(factor, bigint.new(1))
else
result.digits[#result.digits + 1] = factor
end end
-- Subtract the divisor from the dividend to obtain the for i = 0, #factor.digits - 1 do
-- remainder, which is the new dividend for the next loop result.digits[#result.digits + 1 - i] = factor.digits[i + 1]
dividend = bigint.subtract(dividend, end
bigint.subtract(divisor, big2))
-- Reset the divisor
divisor = big2:clone()
end end
-- Remove leading zeros from result
while (result.digits[1] == 0) do
table.remove(result.digits, 1)
end end
-- The remainder of the final loop is returned as the function's
-- overall remainder
return result, dividend return result, dividend
end end
end end

View File

@@ -16,6 +16,9 @@ function love.load()
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
-- used for screenshots
GLOBAL_CANVAS = love.graphics.newCanvas()
-- init config -- init config
if not config.das then config.das = 10 end if not config.das then config.das = 10 end
if not config.arr then config.arr = 2 end if not config.arr then config.arr = 2 end
@@ -108,6 +111,9 @@ function love.update(dt)
end end
function love.draw() function love.draw()
love.graphics.setCanvas(GLOBAL_CANVAS)
love.graphics.clear()
love.graphics.push() love.graphics.push()
-- get offset matrix -- get offset matrix
@@ -123,6 +129,10 @@ function love.draw()
scene:render() scene:render()
love.graphics.pop() love.graphics.pop()
love.graphics.setCanvas()
love.graphics.setColor(1,1,1,1)
love.graphics.draw(GLOBAL_CANVAS)
end end
function love.keypressed(key, scancode) function love.keypressed(key, scancode)
@@ -140,6 +150,14 @@ function love.keypressed(key, scancode)
scene.restart_message = true scene.restart_message = true
if config.secret then playSE("mode_decide") if config.secret then playSE("mode_decide")
else playSE("erase") end else playSE("erase") end
-- f12 is reserved for saving screenshots
elseif scancode == "f12" then
local ss_name = os.date("ss/%Y-%m-%d_%H-%M-%S.png")
if not love.filesystem.getInfo("ss") then
love.filesystem.createDirectory("ss")
end
print("Saving screenshot as "..ss_name)
GLOBAL_CANVAS:newImageData():encode("png", ss_name)
-- function keys are reserved -- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return return
@@ -261,3 +279,8 @@ function love.focus(f)
pauseBGM() pauseBGM()
end end
end end
function love.resize(w, h)
GLOBAL_CANVAS:release()
GLOBAL_CANVAS = love.graphics.newCanvas(w, h)
end

View File

@@ -1,2 +1,2 @@
tar -a -c -f cambridge.zip libs/binser.lua libs/classic.lua libs/simple-slider.lua libs/discordRPC.lua load res scene tetris conf.lua main.lua scene.lua funcs.lua tar -a -c -f cambridge.zip libs load res scene tetris conf.lua main.lua scene.lua funcs.lua
rename cambridge.zip cambridge.love rename cambridge.zip cambridge.love

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -83,6 +83,7 @@ function GameScene:render()
end end
self.game:drawGrid() self.game:drawGrid()
if self.game.lcd > 0 then self.game:drawLineClearAnimation() end
self.game:drawPiece() self.game:drawPiece()
self.game:drawNextQueue(self.ruleset) self.game:drawNextQueue(self.ruleset)
self.game:drawScoringInfo() self.game:drawScoringInfo()
@@ -139,19 +140,8 @@ function GameScene:onInputRelease(e)
end end
function submitHighscore(hash, data) function submitHighscore(hash, data)
function isHighscore(score, high)
for k, _ in pairs(score) do
if not high[k] or score[k] > high[k] then
return true
end
end
return false
end
if not highscores[hash] then highscores[hash] = {} end if not highscores[hash] then highscores[hash] = {} end
if isHighscore(data, highscores[hash]) then table.insert(highscores[hash], data)
highscores[hash] = data
end
saveHighscores() saveHighscores()
end end

View File

@@ -11,7 +11,7 @@ ConfigScene.options = {
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}}, {"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}}, {"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
{"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}}, {"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
{"spawn_positions", "Spawn Positions", false, {"In field", "Out of field"}}, {"spawn_positions", "Spawn Positions", false, {"Per ruleset", "In field", "Out of field"}},
{"display_gamemode", "Display Gamemode", false, {"On", "Off"}}, {"display_gamemode", "Display Gamemode", false, {"On", "Off"}},
{"das_last_key", "DAS Switch", false, {"Default", "Instant"}}, {"das_last_key", "DAS Switch", false, {"Default", "Instant"}},
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}}, {"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
@@ -103,7 +103,7 @@ function ConfigScene:onInputPress(e)
else else
playSE("cursor") playSE("cursor")
sld = self[self.options[self.highlight][4]] sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 3) / (sld.max - sld.min))) sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 5) / (sld.max - sld.min)))
end end
elseif e.input == "right" or e.scancode == "right" then elseif e.input == "right" or e.scancode == "right" then
if not self.options[self.highlight][3] then if not self.options[self.highlight][3] then
@@ -113,7 +113,7 @@ function ConfigScene:onInputPress(e)
else else
playSE("cursor") playSE("cursor")
sld = self[self.options[self.highlight][4]] sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 3) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min)) sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min))
end end
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave() loadSave()

View File

@@ -41,14 +41,14 @@ function ModeSelectScene:render()
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
end end
love.graphics.rectangle("fill", 20, 198, 240, 22) love.graphics.rectangle("fill", 20, 258, 240, 22)
if self.menu_state.select == "mode" then if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
end end
love.graphics.rectangle("fill", 340, 198, 200, 22) love.graphics.rectangle("fill", 340, 258, 200, 22)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@@ -56,36 +56,13 @@ function ModeSelectScene:render()
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do for idx, mode in pairs(game_modes) do
if(idx >= self.menu_state.mode-6 and idx <= self.menu_state.mode+6) then if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
love.graphics.printf(mode.name, 40, (200 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left") love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
end end
end end
for idx, ruleset in pairs(rulesets) do for idx, ruleset in pairs(rulesets) do
if(idx >= self.menu_state.ruleset-6 and idx <= self.menu_state.ruleset+6) then if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
love.graphics.printf(ruleset.name, 360, (200 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left") love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
end
end
-- mode description and highscore
for midx, mode in pairs(game_modes) do
for ridx, ruleset in pairs(rulesets) do
if (midx == self.menu_state.mode) and (ridx == self.menu_state.ruleset) then
love.graphics.printf(
"Mode Description:\n\n" .. mode.tagline, 20, 350, 200, "left"
)
love.graphics.printf(
ruleset.name .. " Highscore:", 240, 350, 200, "right"
)
local highscore_string = ""
if highscores[mode.hash .. "-" .. ruleset.hash] then
for k, v in ipairs(highscores[mode.hash .. "-" .. ruleset.hash]) do
highscore_string = highscore_string .. k .. ": " .. v .. "\n"
end
else
highscore_string = "You don't have any highscores yet!"
end
love.graphics.printf(highscore_string, 450, 350, 200, "left")
end
end end
end end
end end

View File

@@ -111,18 +111,24 @@ function Grid:getClearedRowCount()
end end
function Grid:markClearedRows() function Grid:markClearedRows()
local block_table = {}
for row = 1, self.height do for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
block_table[row] = {}
for x = 1, self.width do for x = 1, self.width do
block_table[row][x] = {
skin = self.grid[row][x].skin,
colour = self.grid[row][x].colour,
}
self.grid[row][x] = { self.grid[row][x] = {
skin = self.grid[row][x].skin, skin = self.grid[row][x].skin,
colour = "X" colour = "X"
} }
self.grid_age[row][x] = 0 --self.grid_age[row][x] = 0
end end
end end
end end
return true return block_table
end end
function Grid:clearClearedRows() function Grid:clearClearedRows()
@@ -388,7 +394,8 @@ end
function Grid:draw() function Grid:draw()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if blocks[self.grid[y][x].skin] and
blocks[self.grid[y][x].skin][self.grid[y][x].colour] then
if self.grid_age[y][x] < 2 then if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
@@ -396,7 +403,8 @@ function Grid:draw()
if self.grid[y][x].skin == "bone" then if self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
elseif self.grid[y][x].colour == "X" then elseif self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15) love.graphics.setColor(0, 0, 0, 0)
--love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
else else
love.graphics.setColor(0.5, 0.5, 0.5, 1) love.graphics.setColor(0.5, 0.5, 0.5, 1)
end end
@@ -405,11 +413,11 @@ function Grid:draw()
if self.grid[y][x].skin ~= "bone" and self.grid[y][x].colour ~= "X" then if self.grid[y][x].skin ~= "bone" and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1) love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@@ -427,18 +435,20 @@ end
function Grid:drawOutline() function Grid:drawOutline()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
--[[
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15) love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
end end
]]
if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= "X" then if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1) love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@@ -459,7 +469,8 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
opacity = 1 - self.grid_age[y][x] / 15 opacity = 0
--opacity = 1 - self.grid_age[y][x] / 15
elseif garbage_opacity_function and self.grid[y][x].colour == "A" then elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
opacity = garbage_opacity_function(self.grid_age[y][x]) opacity = garbage_opacity_function(self.grid_age[y][x])
else else
@@ -471,11 +482,11 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
if opacity > 0 and self.grid[y][x].colour ~= "X" then if opacity > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64) love.graphics.setColor(0.64, 0.64, 0.64)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@@ -506,18 +517,19 @@ function Grid:drawCustom(colour_function, gamestate)
if block ~= empty then if block ~= empty then
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x]) local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
A = 1 - self.grid_age[y][x] / 15 A = 0
--A = 1 - self.grid_age[y][x] / 15
end end
love.graphics.setColor(R, G, B, A) love.graphics.setColor(R, G, B, A)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
if outline > 0 and self.grid[y][x].colour ~= "X" then if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline) love.graphics.setColor(0.64, 0.64, 0.64, outline)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then

View File

@@ -104,9 +104,8 @@ function Piece:dropToBottom(grid)
self:dropSquares(math.huge, grid) self:dropSquares(math.huge, grid)
self.gravity = 0 self.gravity = 0
if self.position.y > piece_y then if self.position.y > piece_y then
-- if it got dropped any, also reset lock delay
if self.ghost == false then playSE("bottom") end if self.ghost == false then playSE("bottom") end
self.lock_delay = 0 -- self.lock_delay = 0
end end
return self return self
end end

View File

@@ -10,6 +10,9 @@ local BagRandomizer = require 'tetris.randomizers.bag'
local GameMode = Object:extend() local GameMode = Object:extend()
GameMode.name = ""
GameMode.hash = ""
GameMode.tagline = ""
GameMode.rollOpacityFunction = function(age) return 0 end GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new(secret_inputs) function GameMode:new(secret_inputs)
@@ -42,6 +45,7 @@ function GameMode:new(secret_inputs)
self.enable_hard_drop = true self.enable_hard_drop = true
self.next_queue_length = 1 self.next_queue_length = 1
self.additive_gravity = true self.additive_gravity = true
self.classic_lock = false
self.draw_section_times = false self.draw_section_times = false
self.draw_secondary_section_times = false self.draw_secondary_section_times = false
self.big_mode = false self.big_mode = false
@@ -60,6 +64,7 @@ function GameMode:new(secret_inputs)
self.hard_drop_locked = false self.hard_drop_locked = false
self.lock_on_soft_drop = false self.lock_on_soft_drop = false
self.lock_on_hard_drop = false self.lock_on_hard_drop = false
self.cleared_block_table = {}
self.used_randomizer = nil self.used_randomizer = nil
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
@@ -76,6 +81,7 @@ function GameMode:getLockDelay() return 30 end
function GameMode:getLineClearDelay() return 40 end function GameMode:getLineClearDelay() return 40 end
function GameMode:getDasLimit() return 15 end function GameMode:getDasLimit() return 15 end
function GameMode:getDasCutDelay() return 0 end function GameMode:getDasCutDelay() return 0 end
function GameMode:getGravity() return 1/64 end
function GameMode:getNextPiece(ruleset) function GameMode:getNextPiece(ruleset)
return { return {
@@ -101,7 +107,7 @@ function GameMode:initialize(ruleset, secret_inputs)
BagRandomizer(ruleset.pieces) BagRandomizer(ruleset.pieces)
) )
) )
for i = 1, self.next_queue_length do for i = 1, math.max(self.next_queue_length, 1) do
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock] self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
@@ -167,14 +173,21 @@ function GameMode:update(inputs, ruleset)
if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then
self:hold(inputs, ruleset) self:hold(inputs, ruleset)
self.prev_inputs = inputs self.prev_inputs = inputs
if not self.grid:canPlacePiece(self.piece) then
self.game_over = true
end
return return
end end
if self.lock_drop and inputs["down"] ~= true then if (self.lock_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["down"] ~= true then
self.drop_locked = false self.drop_locked = false
end end
if self.lock_hard_drop and inputs["up"] ~= true then if (self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["up"] ~= true then
self.hard_drop_locked = false self.hard_drop_locked = false
end end
@@ -186,7 +199,7 @@ function GameMode:update(inputs, ruleset)
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs, inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
self.move, self:getLockDelay(), self:getDropSpeed(), self.move, self:getLockDelay(), self:getDropSpeed(),
self.drop_locked, self.hard_drop_locked, self.drop_locked, self.hard_drop_locked,
self.enable_hard_drop, self.additive_gravity self.enable_hard_drop, self.additive_gravity, self.classic_lock
) )
local piece_dy = self.piece.position.y - piece_y local piece_dy = self.piece.position.y - piece_y
@@ -203,7 +216,7 @@ function GameMode:update(inputs, ruleset)
) then ) then
self.das.frames = math.max( self.das.frames = math.max(
self.das.frames - self:getDasCutDelay(), self.das.frames - self:getDasCutDelay(),
-self:getDasCutDelay() -(self:getDasCutDelay() + 1)
) )
end end
@@ -237,7 +250,7 @@ function GameMode:update(inputs, ruleset)
self:onPieceLock(self.piece, cleared_row_count) self:onPieceLock(self.piece, cleared_row_count)
self:updateScore(self.level, self.drop_bonus, cleared_row_count) self:updateScore(self.level, self.drop_bonus, cleared_row_count)
self.grid:markClearedRows() self.cleared_block_table = self.grid:markClearedRows()
self.piece = nil self.piece = nil
if self.enable_hold then if self.enable_hold then
self.held = false self.held = false
@@ -251,6 +264,7 @@ function GameMode:update(inputs, ruleset)
) )
if self.lcd == 0 then if self.lcd == 0 then
self.grid:clearClearedRows() self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
@@ -287,6 +301,7 @@ function GameMode:onPieceLock(piece, cleared_row_count)
end end
function GameMode:onLineClear(cleared_row_count) end function GameMode:onLineClear(cleared_row_count) end
function GameMode:afterLineClear(cleared_row_count) end
function GameMode:onPieceEnter() end function GameMode:onPieceEnter() end
function GameMode:onHold() end function GameMode:onHold() end
@@ -418,7 +433,9 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
self.lcd = self.lcd - 1 self.lcd = self.lcd - 1
self:areCancel(inputs, ruleset) self:areCancel(inputs, ruleset)
if self.lcd == 0 then if self.lcd == 0 then
local cleared_row_count = self.grid:getClearedRowCount()
self.grid:clearClearedRows() self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
playSE("fall") playSE("fall")
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
@@ -506,10 +523,14 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
if self.buffer_soft_drop then if self.buffer_soft_drop then
self.buffer_soft_drop = false self.buffer_soft_drop = false
end end
if self.lock_drop then if self.lock_drop or (
not ruleset.are or self:getARE() == 0
) then
self.drop_locked = true self.drop_locked = true
end end
if self.lock_hard_drop then if self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
) then
self.hard_drop_locked = true self.hard_drop_locked = true
end end
if generate_next_piece == nil then if generate_next_piece == nil then
@@ -529,14 +550,87 @@ function GameMode:getHighScoreData()
} }
end end
function GameMode:animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self:getLineClearDelay()),
skin, colour,
48 + x * 16, y * 16
}
end
function GameMode:drawLineClearAnimation()
-- animation function
-- params: block x, y, skin, colour
-- returns: table with RGBA, skin, colour, x, y
-- Fadeout (default)
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self:getLineClearDelay()),
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- Flash
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
self.lcd % 6 < 3 and 1 or 0.25,
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- TGM1 pop-out
--[[
function animation(x, y, skin, colour)
local p = 0.5
local l = (
(self:getLineClearDelay() - self.lcd) / self:getLineClearDelay()
)
local dx = l * (x - (1 + self.grid.width) / 2)
local dy = l * (y - (1 + self.grid.height) / 2)
return {
1, 1, 1, 1, skin, colour,
48 + (x + dx) * 16,
(y + dy) * 16 + (464 / (p - 1)) * l * (p - l)
}
end
--]]
for y, row in pairs(self.cleared_block_table) do
for x, block in pairs(row) do
local animation_table = self:animation(x, y, block.skin, block.colour)
love.graphics.setColor(
animation_table[1], animation_table[2],
animation_table[3], animation_table[4]
)
love.graphics.draw(
blocks[animation_table[5]][animation_table[6]],
animation_table[7], animation_table[8]
)
end
end
end
function GameMode:drawPiece() function GameMode:drawPiece()
if self.piece ~= nil then if self.piece ~= nil then
self.piece:draw( local b = (
1, self.classic_lock and
self:getLockDelay() == 0 and 1 or (
(0.25 + 0.75 * math.max(1 - self.piece.gravity, 1 - (self.piece.lock_delay / self:getLockDelay()))), self.piece:isDropBlocked(self.grid) and
self.grid 1 - self.piece.gravity or 1
) or
1 - (self.piece.lock_delay / self:getLockDelay())
) )
self.piece:draw(1, 0.25 + 0.75 * b, self.grid)
end end
end end
@@ -589,6 +683,18 @@ function GameMode:setHoldOpacity()
love.graphics.setColor(colour, colour, colour, 1) love.graphics.setColor(colour, colour, colour, 1)
end end
function GameMode:getBackground()
return 0
end
function GameMode:getHighscoreData()
return {}
end
function GameMode:drawGrid()
self.grid:draw()
end
function GameMode:drawScoringInfo() function GameMode:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
@@ -657,7 +763,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
end end
end end
function GameMode:drawSectionTimesWithSplits(current_section) function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
section_limit = section_limit or math.huge
local section_x = 440 local section_x = 440
local split_x = 530 local split_x = 530
@@ -671,8 +779,11 @@ function GameMode:drawSectionTimesWithSplits(current_section)
end end
end end
if (current_section <= section_limit) then
love.graphics.setColor(self:sectionColourFunction(current_section))
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left") 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") love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left")
end
end end
function GameMode:drawCustom() end function GameMode:drawCustom() end

View File

@@ -171,7 +171,6 @@ function MarathonA2Game:updateSectionTimes(old_level, new_level)
self.section_start_time = self.frames self.section_start_time = self.frames
self.section_tetrises[math.floor(old_level / 100)] = self.tetris_count self.section_tetrises[math.floor(old_level / 100)] = self.tetris_count
self.tetris_count = 0 self.tetris_count = 0
print(self.section_tetrises[math.floor(old_level / 100)])
end end
end end

View File

@@ -437,6 +437,8 @@ function SakuraGame:advanceOneFrame(inputs, ruleset)
return true return true
end end
function SakuraGame:onGameComplete() end
local function colourXRay(game, block, x, y, age) local function colourXRay(game, block, x, y, age)
local r, g, b, a = .5,.5,.5 local r, g, b, a = .5,.5,.5
if ((game.stage_frames/2 - x) % 30 < 1) if ((game.stage_frames/2 - x) % 30 < 1)
@@ -468,9 +470,9 @@ function SakuraGame:drawGrid()
self.grid:drawCustom(colourColor, self) self.grid:drawCustom(colourColor, self)
else else
self.grid:draw() self.grid:draw()
-- if self.piece ~= nil and self.level < 100 then end
self:drawGhostPiece(ruleset) if self.piece ~= nil and self.level < 100 then
-- end self:drawGhostPiece()
end end
end end

View File

@@ -66,24 +66,24 @@ function SurvivalA1Game:getGravity()
end end
local function getRankForScore(score) local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400, grade = 0} if score < 400 then return {rank = "9", next = 400}
elseif score < 800 then return {rank = "8", next = 800, grade = 1} elseif score < 800 then return {rank = "8", next = 800}
elseif score < 1400 then return {rank = "7", next = 1400, grade = 2} elseif score < 1400 then return {rank = "7", next = 1400}
elseif score < 2000 then return {rank = "6", next = 2000, grade = 3} elseif score < 2000 then return {rank = "6", next = 2000}
elseif score < 3500 then return {rank = "5", next = 3500, grade = 4} elseif score < 3500 then return {rank = "5", next = 3500}
elseif score < 5500 then return {rank = "4", next = 5500, grade = 5} elseif score < 5500 then return {rank = "4", next = 5500}
elseif score < 8000 then return {rank = "3", next = 8000, grade = 6} elseif score < 8000 then return {rank = "3", next = 8000}
elseif score < 12000 then return {rank = "2", next = 12000, grade = 7} elseif score < 12000 then return {rank = "2", next = 12000}
elseif score < 16000 then return {rank = "1", next = 16000, grade = 8} elseif score < 16000 then return {rank = "1", next = 16000}
elseif score < 22000 then return {rank = "S1", next = 22000, grade = 9} elseif score < 22000 then return {rank = "S1", next = 22000}
elseif score < 30000 then return {rank = "S2", next = 30000, grade = 10} elseif score < 30000 then return {rank = "S2", next = 30000}
elseif score < 40000 then return {rank = "S3", next = 40000, grade = 11} elseif score < 40000 then return {rank = "S3", next = 40000}
elseif score < 52000 then return {rank = "S4", next = 52000, grade = 12} elseif score < 52000 then return {rank = "S4", next = 52000}
elseif score < 66000 then return {rank = "S5", next = 66000, grade = 13} elseif score < 66000 then return {rank = "S5", next = 66000}
elseif score < 82000 then return {rank = "S6", next = 82000, grade = 14} elseif score < 82000 then return {rank = "S6", next = 82000}
elseif score < 100000 then return {rank = "S7", next = 100000, grade = 15} elseif score < 100000 then return {rank = "S7", next = 100000}
elseif score < 120000 then return {rank = "S8", next = 120000, grade = 16} elseif score < 120000 then return {rank = "S8", next = 120000}
else return {rank = "S9", next = "???", grade = 17} else return {rank = "S9", next = "???"}
end end
end end
@@ -208,15 +208,10 @@ end
function SurvivalA1Game:getHighscoreData() function SurvivalA1Game:getHighscoreData()
return { return {
grade = ( grade = self.grade,
(self.gm_conditions["level300"] and
self.gm_conditions["level500"] and
self.gm_conditions["level999"]) and
18 or getRankForScore(self.score).grade
),
frames = self.frames,
score = self.score, score = self.score,
level = self.level, level = self.level,
frames = self.frames,
} }
end end

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

@@ -18,6 +18,7 @@ ARS.colourscheme = {
ARS.softdrop_lock = false ARS.softdrop_lock = false
ARS.harddrop_lock = true ARS.harddrop_lock = true
ARS.spawn_above_field = true
function ARS:onPieceCreate(piece, grid) function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0 piece.floorkick = 0
@@ -49,4 +50,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

@@ -5,6 +5,7 @@ local ARS = Ruleset:extend()
ARS.name = "ACE-ARS2" ARS.name = "ACE-ARS2"
ARS.hash = "ArikaACE2" ARS.hash = "ArikaACE2"
ARS.spawn_above_field = true
function ARS:onPieceCreate(piece, grid) function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0 piece.floorkick = 0
@@ -36,4 +37,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,20 @@ 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.spawn_above_field = 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,7 +364,7 @@ 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

View File

@@ -23,6 +23,7 @@ Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false Ruleset.enable_IRS_wallkicks = false
Ruleset.are_cancel = false Ruleset.are_cancel = false
Ruleset.are = true Ruleset.are = true
Ruleset.spawn_above_field = false
Ruleset.next_sounds = { Ruleset.next_sounds = {
I = "I", I = "I",
@@ -208,8 +209,11 @@ function Ruleset:dropPiece(
end end
end end
function Ruleset:lockPiece(piece, grid, lock_delay) function Ruleset:lockPiece(piece, grid, lock_delay, classic_lock)
if piece:isDropBlocked(grid) and piece.gravity >= 1 and piece.lock_delay >= lock_delay then if piece:isDropBlocked(grid) and (
(classic_lock and piece.gravity >= 1) or
(not classic_lock and piece.lock_delay >= lock_delay)
) then
piece.locked = true piece.locked = true
end end
end end
@@ -243,10 +247,17 @@ function Ruleset:initializePiece(
end end
end end
local spawn_dy = ( local spawn_dy
config.gamesettings.spawn_positions == 2 and if (config.gamesettings.spawn_positions == 1) then
spawn_dy = (
self.spawn_above_field and 2 or 0
)
else
spawn_dy = (
config.gamesettings.spawn_positions == 3 and
2 or 0 2 or 0
) )
end
local piece = Piece(data.shape, data.orientation - 1, { local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_x and spawn_x or spawn_positions[data.shape].x, x = spawn_x and spawn_x or spawn_positions[data.shape].x,
@@ -278,7 +289,7 @@ function Ruleset:processPiece(
inputs, piece, grid, gravity, prev_inputs, inputs, piece, grid, gravity, prev_inputs,
move, lock_delay, drop_speed, move, lock_delay, drop_speed,
drop_locked, hard_drop_locked, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity hard_drop_enabled, additive_gravity, classic_lock
) )
local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed] local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed]
@@ -294,7 +305,7 @@ function Ruleset:processPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked, inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity hard_drop_enabled, additive_gravity
) )
self:lockPiece(piece, grid, lock_delay) self:lockPiece(piece, grid, lock_delay, classic_lock)
end end
function Ruleset:onPieceMove(piece) end function Ruleset:onPieceMove(piece) end

View File

@@ -0,0 +1,102 @@
local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.standard_exp'
local SRS = Ruleset:extend()
SRS.name = "Guideline SRS"
SRS.hash = "Standard"
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.MANIPULATIONS_MAX = 15
SRS.wallkicks_line = {
[0]={
[1]={{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=0},{x=2,y=0},{x=0,y=1}},
[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=2},{x=0,y=-1},{x=0,y=-2},{x=-1,y=0}},
},
[2]={
[0]={{x=1,y=0},{x=2,y=0},{x=-1,y=0},{x=-2,y=0},{x=0,y=-1}},
[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=2},{x=0,y=-1},{x=0,y=-2},{x=1,y=0}},
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
},
};
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:checkNewLow(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.rotations = 0
piece.lowest_y = y
end
end
end
function SRS:onPieceDrop(piece, grid)
self:checkNewLow(piece)
if piece.manipulations >= self.MANIPULATIONS_MAX 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 >= SRS.MANIPULATIONS_MAX then
piece.locked = true
end
end
end
function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset
self:checkNewLow(piece)
piece.manipulations = piece.manipulations + 1
if piece:isDropBlocked(grid) then
if piece.manipulations >= SRS.MANIPULATIONS_MAX then
piece.locked = true
end
end
end
return SRS

View File

@@ -3,46 +3,37 @@ 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 = true
SRS.harddrop_lock = false
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
end end
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}},
},
};
-- Component functions. -- Component functions.
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid) function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
@@ -72,6 +63,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 +80,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 >= SRS.MANIPULATIONS_MAX then
piece.locked = true piece.locked = true
end end
end end
@@ -97,8 +89,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 if piece.rotations >= self.ROTATIONS_MAX then
if piece.manipulations >= self.MANIPULATIONS_MAX then piece.rotations = piece.rotations + 1
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",
@@ -89,22 +89,22 @@ SRS.block_offsets = {
SRS.wallkicks_3x3 = { SRS.wallkicks_3x3 = {
[0]={ [0]={
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}}, [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}}, [2]={{x=1,y=0},{x=2,y=0},{x=1,y=1},{x=2,y=1},{x=-1,y=0},{x=-2,y=0},{x=-1,y=1},{x=-2,y=1},{x=0,y=-1},{x=3,y=0},{x=-3,y=0}},
[3]={{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}},
}, },
[1]={ [1]={
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}}, [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}}, [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}}, [3]={{x=0,y=1},{x=0,y=2},{x=-1,y=1},{x=-1,y=2},{x=0,y=-1},{x=0,y=-2},{x=-1,y=-1},{x=-1,y=-2},{x=1,y=0},{x=0,y=3},{x=0,y=-3}},
}, },
[2]={ [2]={
[0]={{x=0, y=1}, {x=0, y=-1}}, [0]={{x=-1,y=0},{x=-2,y=0},{x=-1,y=-1},{x=-2,y=-1},{x=1,y=0},{x=2,y=0},{x=1,y=-1},{x=2,y=-1},{x=0,y=1},{x=-3,y=0},{x=3,y=0}},
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}}, [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]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
}, },
[3]={ [3]={
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}}, [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}}, [1]={{x=0,y=1},{x=0,y=2},{x=1,y=1},{x=1,y=2},{x=0,y=-1},{x=0,y=-2},{x=1,y=-1},{x=1,y=-2},{x=-1,y=0},{x=0,y=3},{x=0,y=-3}},
[2]={{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}},
}, },
}; };
@@ -112,22 +112,22 @@ SRS.wallkicks_3x3 = {
SRS.wallkicks_line = { SRS.wallkicks_line = {
[0]={ [0]={
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}}, [1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
[2]={}, [2]={{x=-1,y=0},{x=-2,y=0},{x=1,y=0},{x=2,y=0},{x=0,y=1}},
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}}, [3]={{x= 2, y= 0}, {x=-1, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
}, },
[1]={ [1]={
[0]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 2}}, [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}}, [2]={{x=-1, y= 0}, {x= 2, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
[3]={}, [3]={{x=0,y=1},{x=0,y=2},{x=0,y=-1},{x=0,y=-2},{x=-1,y=0}},
}, },
[2]={ [2]={
[0]={}, [0]={{x=1,y=0},{x=2,y=0},{x=-1,y=0},{x=-2,y=0},{x=0,y=-1}},
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 1}}, [1]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 1}},
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}}, [3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}},
}, },
[3]={ [3]={
[0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}}, [0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}},
[1]={}, [1]={{x=0,y=1},{x=0,y=2},{x=0,y=-1},{x=0,y=-2},{x=1,y=0}},
[2]={{x= 1, y= 0}, {x=-2, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}}, [2]={{x= 1, y= 0}, {x=-2, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
}, },
}; };
@@ -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