Compare commits
206 Commits
v0.3-beta1
...
ae1231c47a
| Author | SHA1 | Date | |
|---|---|---|---|
| ae1231c47a | |||
|
|
366ac1d552 | ||
| 302353f716 | |||
|
|
7f550b629f | ||
|
|
6b2252e6d9 | ||
|
|
e1741440f2 | ||
|
|
c56f290921 | ||
|
|
86e975f929 | ||
|
|
9f8e9a9778 | ||
|
|
62f9475fa9 | ||
|
|
99d3732d00 | ||
|
|
f5121b62e5 | ||
|
|
cbdbfa6633 | ||
|
|
1b1abc9792 | ||
|
|
894e99e677 | ||
|
|
d4b619da89 | ||
|
|
3766149cb7 | ||
|
|
449ca16bc4 | ||
|
|
71d76e8a6b | ||
|
|
3bdc6e1b2d | ||
|
|
d9b6c85704 | ||
|
|
5ce0686e1a | ||
|
|
3cf496ba98 | ||
|
|
5ddc6ec561 | ||
|
|
b91ffc913b | ||
|
|
ab445ff699 | ||
|
|
7b7a255bf8 | ||
|
|
57721ed35d | ||
|
|
8383d3f445 | ||
|
|
116284f31c | ||
|
|
2189e3a7b8 | ||
|
|
b1d325b714 | ||
|
|
4ab5e3747a | ||
|
|
9761ead48f | ||
|
|
8dedc8a70e | ||
|
|
21769f21c8 | ||
|
|
5cf26b4500 | ||
|
|
4b4a968632 | ||
|
|
e0d98de50d | ||
|
|
4992ea733c | ||
|
|
30ca434027 | ||
|
|
502a50d004 | ||
|
|
36f5287a39 | ||
|
|
a9bbe4a08d | ||
|
|
ee431f5fd8 | ||
|
|
36f2672e06 | ||
|
|
6ecea7edb1 | ||
|
|
dc764b9177 | ||
|
|
5a1494cb5a | ||
|
|
684c4f5b78 | ||
|
|
b568c0fe69 | ||
|
|
2ea75cdfaf | ||
|
|
1f0b43f1b7 | ||
|
|
40bdc5ed99 | ||
|
|
33f2a96ae8 | ||
|
|
846013ce7a | ||
|
|
37c85adc75 | ||
|
|
e7bb44deb4 | ||
|
|
57518dc299 | ||
|
|
0453a3db97 | ||
|
|
b85de17e51 | ||
|
|
163b8f6cc5 | ||
|
|
7250bee619 | ||
|
|
83de216408 | ||
|
|
ba235c8a41 | ||
|
|
ca18d090c9 | ||
|
|
a3a27d2566 | ||
|
|
b15cd9802f | ||
|
|
4c4a818c5c | ||
|
|
716de2814b | ||
|
|
bf19f49323 | ||
|
|
1234e78354 | ||
|
|
9129503d54 | ||
|
|
eae58f11e9 | ||
|
|
3cf5daeb2e | ||
|
|
1dfe68ccff | ||
|
|
8a459b68ba | ||
|
|
cb2b693bcb | ||
|
|
ef6d156d38 | ||
|
|
83e498534c | ||
|
|
8f19c73e2a | ||
|
|
e36b855ff7 | ||
|
|
23b58951cb | ||
|
|
1d73916b7c | ||
|
|
3947e9f02f | ||
|
|
99b15803ee | ||
|
|
d350b25726 | ||
|
|
44e4d00172 | ||
|
|
31e2529265 | ||
|
|
ea7c75f0b3 | ||
|
|
714c6b5e99 | ||
|
|
6a5d5a9c88 | ||
|
|
03491ba151 | ||
|
|
6e22e3d15b | ||
|
|
66ab5992ad | ||
|
|
2c07c2a58c | ||
|
|
a4d3f3bffc | ||
|
|
9ac60cbb5e | ||
|
|
cdd846c3e6 | ||
|
|
33d260b753 | ||
|
|
1644fcdf8e | ||
|
|
f3c1cf6e1f | ||
|
|
06a8a2ebf7 | ||
|
|
15354ce004 | ||
|
|
af02cd3467 | ||
|
|
acb05918c1 | ||
|
|
b644c8e457 | ||
|
|
288961e12a | ||
|
|
a047e51681 | ||
|
|
77f24f5ee5 | ||
|
|
32c2274bef | ||
|
|
4920e5de1c | ||
|
|
8418fc8ab7 | ||
|
|
711a5120f1 | ||
|
|
e7c3c9446a | ||
|
|
3ac39acd7a | ||
|
|
d0505251b3 | ||
|
|
bb0fe2ac20 | ||
|
|
986ebac47f | ||
|
|
9799147f96 | ||
|
|
1dda12e4be | ||
|
|
38947e00c0 | ||
|
|
035f6dd7b4 | ||
|
|
aa3eadc93d | ||
|
|
cb6962825f | ||
|
|
b5e7ce5be6 | ||
|
|
1ccd6a09d3 | ||
|
|
5a074f77cf | ||
|
|
81677221f1 | ||
|
|
a998be6f7b | ||
|
|
9c1c8eea21 | ||
|
|
f022c6c4b7 | ||
|
|
38f3d23b95 | ||
|
|
816d27db39 | ||
|
|
ce08ffd3da | ||
|
|
f0e84a8874 | ||
|
|
5e02471fb4 | ||
|
|
fa2fe77081 | ||
|
|
682c4a485a | ||
|
|
68760105cc | ||
|
|
e19da98ea1 | ||
|
|
e8904b92ed | ||
|
|
4f574e7716 | ||
|
|
f1528e8d71 | ||
|
|
79a25c3954 | ||
|
|
0f3883e18d | ||
|
|
1acd0ec65a | ||
|
|
b22f671409 | ||
|
|
0b6f62d50e | ||
|
|
086f327371 | ||
|
|
3c83ae0bf4 | ||
|
|
450833b246 | ||
|
|
8e7a5418dc | ||
|
|
6609b642dc | ||
|
|
452879ebab | ||
|
|
70a827b477 | ||
|
|
d281a732db | ||
|
|
01e91fbd93 | ||
|
|
ece853c9d3 | ||
|
|
ea8d008370 | ||
|
|
e20eb048c8 | ||
|
|
a33ca1af24 | ||
|
|
664bca2282 | ||
|
|
fc8fb8b66f | ||
|
|
fc58e6e908 | ||
|
|
061f6f5164 | ||
|
|
4e9cea7dda | ||
|
|
fa97216167 | ||
|
|
3f8d68cc9d | ||
|
|
6639d73c1c | ||
|
|
668f061077 | ||
|
|
cb70967b82 | ||
|
|
0c2ba5f0cc | ||
|
|
6d07a3b820 | ||
|
|
2de13a97f0 | ||
|
|
512c2149f0 | ||
|
|
6fb19220b7 | ||
|
|
08da67c434 | ||
|
|
2d63ca8ee1 | ||
|
|
0f09d47e60 | ||
|
|
9d44d1e771 | ||
|
|
5d022f9037 | ||
|
|
818743fe77 | ||
|
|
f22424d671 | ||
|
|
dd6baf1fe6 | ||
|
|
11cf5a9d55 | ||
|
|
5642ed1326 | ||
|
|
c0888c484f | ||
|
|
3ef3b193fd | ||
|
|
0c2e3efd1a | ||
|
|
5076adf022 | ||
|
|
1a75d983dc | ||
|
|
5b8e9586bd | ||
|
|
7d7dd8c3c2 | ||
|
|
29afdcecfc | ||
|
|
8b09833ae6 | ||
|
|
64047eaf9c | ||
|
|
125488b4d9 | ||
|
|
1fdd091456 | ||
|
|
ced40297cc | ||
|
|
32f2a0b3e7 | ||
|
|
dd5347ad8d | ||
|
|
b732ebb213 | ||
|
|
84634d6933 | ||
|
|
0d13a9f236 | ||
|
|
45120bc9f7 |
53
README.md
@@ -1,18 +1,15 @@
|
||||

|
||||
|
||||
Important notice
|
||||
================
|
||||
|
||||

|
||||

|
||||
|
||||
Cambridge
|
||||
=========
|
||||
|
||||
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 [Milla](https://github.com/MillaBasset), [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
|
||||
The Discord server has been reopened! https://discord.gg/AADZUmgsph
|
||||
|
||||
The game also has a website now with more detail than seen on this README: https://t-sp.in/cambridge
|
||||
|
||||
Credits
|
||||
-------
|
||||
@@ -22,19 +19,7 @@ Credits
|
||||
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||
- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
|
||||
|
||||
The following people in no particular order also helped with the project:
|
||||
- [Hailey](https://github.com/haileylgbt)
|
||||
- CylinderKnot
|
||||
- MarkGamed7794
|
||||
- [Mizu](https://github.com/rexxt)
|
||||
- MattMayuga
|
||||
- Kitaru
|
||||
- switchpalacecorner
|
||||
- [sinefuse](https://github.com/sinefuse)
|
||||
- [2Tie](https://github.com/2Tie)
|
||||
- [nightmareci](https://github.com/nightmareci)
|
||||
- [MyPasswordIsWeak](https://github.com/MyPasswordIsWeak)
|
||||
- [Dr Ocelot](https://github.com/Dr-Ocelot)
|
||||
More special thanks can be found in-game, under the "Credits" menu.
|
||||
|
||||

|
||||
|
||||
@@ -45,9 +30,15 @@ Playing the game
|
||||
|
||||
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
|
||||
|
||||
If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
|
||||
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/MillaBasset/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/MillaBasset/cambridge/archive/master.zip).
|
||||
|
||||
Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
|
||||
|
||||
@@ -65,9 +56,17 @@ Then, check the mod pack section at the bottom of this page.
|
||||
|
||||
If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!**
|
||||
|
||||
#### Downloading a release
|
||||
|
||||
You can download the .love file in the latest release, and run it with:
|
||||
|
||||
love cambridge.love
|
||||
|
||||
#### Installing from source
|
||||
|
||||
Clone the repository in git:
|
||||
|
||||
git clone https://github.com/SashLilac/cambridge
|
||||
git clone https://github.com/MillaBasset/cambridge
|
||||
|
||||
Alternatively, download the source code ZIP in the latest release.
|
||||
|
||||
@@ -79,11 +78,7 @@ It should run automatically!
|
||||
|
||||
## 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.
|
||||
|
||||
**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.
|
||||
For instructions on how to install modpacks, go to [this](https://github.com/MillaBasset/cambridge-modpack) mod pack to get a taste of the mod potential.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
32
SOURCES.md
@@ -139,4 +139,34 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
bigint.lua (https://github.com/empyreuma/bigint.lua)
|
||||
--------------------
|
||||
|
||||
3-Clause BSD License
|
||||
|
||||
Copyright (c) Emily "empyreuma" 2016
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
conf.lua
@@ -6,5 +6,6 @@ function love.conf(t)
|
||||
t.window.title = "Cambridge"
|
||||
t.window.width = 640
|
||||
t.window.height = 480
|
||||
t.window.icon = "res/img/cambridge_icon.png"
|
||||
t.window.vsync = false
|
||||
end
|
||||
|
||||
@@ -3,19 +3,11 @@ Game modes
|
||||
|
||||
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.
|
||||
* A1 - Tetris The Grand Master (the original from 1998).
|
||||
* A2 - Tetris The Absolute The Grand Master 2 PLUS.
|
||||
* A3 - Tetris The Grand Master 3 Terror-Instinct.
|
||||
* 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
|
||||
--------
|
||||
@@ -28,8 +20,6 @@ From other games:
|
||||
* **MARATHON A1**: Tetris the Grand Master 1.
|
||||
* **MARATHON A2**: Tetris the Grand Master 2 (TAP Master).
|
||||
* **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
|
||||
@@ -43,14 +33,7 @@ From other games:
|
||||
* **SURVIVAL A1**: 20G mode from Tetris the Grand Master.
|
||||
* **SURVIVAL A2**: T.A. Death.
|
||||
* **SURVIVAL A3**: Ti Shirase.
|
||||
|
||||
|
||||
RACE
|
||||
----
|
||||
|
||||
Modes with no levels, just a single timed goal.
|
||||
|
||||
* **Race 40**: How fast can you clear 40 lines? No limits, no holds barred.
|
||||
* **SURVIVAL AX**: Another mode from TGM Ace.
|
||||
|
||||
|
||||
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?
|
||||
|
||||
* **TetrisGram™ Pacer Test**: is a multi-stage piece-placing ability test that progressively gets more difficult as it continues.
|
||||
|
||||
* **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?
|
||||
* **Big A2**: Marathon A2 but all the pieces are BIG!
|
||||
34
funcs.lua
@@ -56,9 +56,19 @@ end
|
||||
|
||||
function formatBigNum(number)
|
||||
-- returns a string representing a number with commas as thousands separator (e.g. 12,345,678)
|
||||
local s = string.format("%d", number)
|
||||
local pos = string.len(s) % 3
|
||||
if pos == 0 then pos = 3 end
|
||||
local s
|
||||
if type(number) == "number" then
|
||||
s = string.format("%d", number)
|
||||
elseif type(number) == "string" then
|
||||
if not tonumber(number) then
|
||||
return
|
||||
else
|
||||
s = number
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
local pos = Mod1(string.len(s), 3)
|
||||
return string.sub(s, 1, pos)
|
||||
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
|
||||
end
|
||||
@@ -66,4 +76,20 @@ end
|
||||
function Mod1(n, m)
|
||||
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
|
||||
return ((n-1) % m) + 1
|
||||
end
|
||||
end
|
||||
|
||||
function table.contains(table, element)
|
||||
for _, value in pairs(table) do
|
||||
if value == element then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function clamp(x, min, max)
|
||||
if max < min then
|
||||
min, max = max, min
|
||||
end
|
||||
return x < min and min or (x > max and max or x)
|
||||
end
|
||||
|
||||
566
libs/bigint/bigint.lua
Normal file
@@ -0,0 +1,566 @@
|
||||
#!/usr/bin/env lua
|
||||
-- If this variable is true, then strict type checking is performed for all
|
||||
-- operations. This may result in slower code, but it will allow you to catch
|
||||
-- errors and bugs earlier.
|
||||
local strict = false
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local bigint = {}
|
||||
|
||||
local mt = {
|
||||
__add = function(lhs, rhs)
|
||||
return bigint.add(lhs, rhs)
|
||||
end,
|
||||
__unm = function()
|
||||
return bigint.negate(self)
|
||||
end,
|
||||
__sub = function(lhs, rhs)
|
||||
return bigint.subtract(lhs, rhs)
|
||||
end,
|
||||
__mul = function(lhs, rhs)
|
||||
return bigint.multiply(lhs, rhs)
|
||||
end,
|
||||
__div = function(lhs, rhs)
|
||||
return bigint.divide(lhs, rhs)
|
||||
end,
|
||||
__mod = function(lhs, rhs)
|
||||
return bigint.modulus(lhs, rhs)
|
||||
end,
|
||||
__pow = function(lhs, rhs)
|
||||
return bigint.exponentiate(lhs, rhs)
|
||||
end,
|
||||
__tostring = function()
|
||||
return bigint.unserialize(self, "s")
|
||||
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
|
||||
}
|
||||
|
||||
local named_powers = require("libs.bigint.named-powers-of-ten")
|
||||
|
||||
-- Create a new bigint or convert a number or string into a big
|
||||
-- Returns an empty, positive bigint if no number or string is given
|
||||
function bigint.new(num)
|
||||
local self = {
|
||||
sign = "+",
|
||||
digits = {}
|
||||
}
|
||||
|
||||
-- Return a new bigint with the same sign and digits
|
||||
function self:clone()
|
||||
local newint = bigint.new()
|
||||
newint.sign = self.sign
|
||||
for _, digit in pairs(self.digits) do
|
||||
newint.digits[#newint.digits + 1] = digit
|
||||
end
|
||||
return newint
|
||||
end
|
||||
|
||||
setmetatable(self, mt)
|
||||
|
||||
if (num) then
|
||||
local num_string = tostring(num)
|
||||
for digit in string.gmatch(num_string, "[0-9]") do
|
||||
table.insert(self.digits, tonumber(digit))
|
||||
end
|
||||
if string.sub(num_string, 1, 1) == "-" then
|
||||
self.sign = "-"
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- Check the type of a big
|
||||
-- Normally only runs when global variable "strict" == true, but checking can be
|
||||
-- forced by supplying "true" as the second argument.
|
||||
function bigint.check(big, force)
|
||||
if (strict or force) then
|
||||
assert(getmetatable(big) == mt, "at least one arg is not a bigint")
|
||||
assert(#big.digits > 0, "bigint is empty")
|
||||
assert(big.sign == "+" or big.sign == "-", "bigint is unsigned")
|
||||
for _, digit in pairs(big.digits) do
|
||||
assert(type(digit) == "number", "at least one digit is invalid")
|
||||
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
|
||||
return true
|
||||
end
|
||||
|
||||
-- Return a new big with the same digits but with a positive sign (absolute
|
||||
-- value)
|
||||
function bigint.abs(big)
|
||||
bigint.check(big)
|
||||
local result = big:clone()
|
||||
result.sign = "+"
|
||||
return result
|
||||
end
|
||||
|
||||
-- Return a new big with the same digits but the opposite sign (negation)
|
||||
function bigint.negate(big)
|
||||
bigint.check(big)
|
||||
local result = big:clone()
|
||||
if (result.sign == "+") then
|
||||
result.sign = "-"
|
||||
else
|
||||
result.sign = "+"
|
||||
end
|
||||
return result
|
||||
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
|
||||
function bigint.unserialize(big, output_type, precision)
|
||||
bigint.check(big)
|
||||
|
||||
local num = ""
|
||||
if big.sign == "-" then
|
||||
num = "-"
|
||||
end
|
||||
|
||||
|
||||
if ((output_type == nil)
|
||||
or (output_type == "number")
|
||||
or (output_type == "n")
|
||||
or (output_type == "string")
|
||||
or (output_type == "s")) then
|
||||
-- Unserialization to a string or number requires reconstructing the
|
||||
-- entire number
|
||||
|
||||
for _, digit in pairs(big.digits) do
|
||||
num = num .. math.floor(digit) -- lazy way of getting rid of .0$
|
||||
end
|
||||
|
||||
if ((output_type == nil)
|
||||
or (output_type == "number")
|
||||
or (output_type == "n")) then
|
||||
return tonumber(num)
|
||||
else
|
||||
return num
|
||||
end
|
||||
|
||||
else
|
||||
-- Unserialization to human-readable form or scientific notation only
|
||||
-- requires reading the first few digits
|
||||
if (precision == nil) then
|
||||
precision = math.min(#big.digits, 3)
|
||||
else
|
||||
assert(precision > 0, "Precision cannot be less than 1")
|
||||
assert(math.floor(precision) == precision,
|
||||
"Precision must be a positive integer")
|
||||
end
|
||||
|
||||
-- num is the first (precision + 1) digits, the first being separated by
|
||||
-- a decimal point from the others
|
||||
num = num .. math.floor(big.digits[1])
|
||||
if (precision > 1) then
|
||||
num = num .. "."
|
||||
for i = 1, (precision - 1) do
|
||||
num = num .. math.floor(big.digits[i + 1])
|
||||
end
|
||||
end
|
||||
|
||||
if ((output_type == "human-readable")
|
||||
or (output_type == "human")
|
||||
or (output_type == "h"))
|
||||
and (#big.digits >= 3 and #big.digits <= 10002) then
|
||||
-- Human-readable output contributed by 123eee555
|
||||
|
||||
local name
|
||||
local walkback = 0 -- Used to enumerate "ten", "hundred", etc
|
||||
|
||||
-- Walk backwards in the index of named_powers starting at the
|
||||
-- number of digits of the input until the first value is found
|
||||
for i = (#big.digits - 1), (#big.digits - 4), -1 do
|
||||
name = named_powers[i]
|
||||
if (name) then
|
||||
if (walkback == 1) then
|
||||
name = "ten " .. name
|
||||
elseif (walkback == 2) then
|
||||
name = "hundred " .. name
|
||||
end
|
||||
break
|
||||
else
|
||||
walkback = walkback + 1
|
||||
end
|
||||
end
|
||||
|
||||
return num .. " " .. name
|
||||
|
||||
else
|
||||
return num .. "*10^" .. (#big.digits - 1)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Basic comparisons
|
||||
-- Accepts symbols (<, >=, ~=) and Unix shell-like options (lt, ge, ne)
|
||||
function bigint.compare(big1, big2, comparison)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
|
||||
local greater = false -- If big1.digits > big2.digits
|
||||
local equal = false
|
||||
|
||||
if (big1.sign == "-") and (big2.sign == "+") then
|
||||
greater = false
|
||||
elseif (#big1.digits > #big2.digits)
|
||||
or ((big1.sign == "+") and (big2.sign == "-")) then
|
||||
greater = true
|
||||
elseif (#big1.digits == #big2.digits) then
|
||||
-- Walk left to right, comparing digits
|
||||
for digit = 1, #big1.digits do
|
||||
if (big1.digits[digit] > big2.digits[digit]) then
|
||||
greater = true
|
||||
break
|
||||
elseif (big2.digits[digit] > big1.digits[digit]) then
|
||||
break
|
||||
elseif (digit == #big1.digits)
|
||||
and (big1.digits[digit] == big2.digits[digit]) then
|
||||
equal = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- If both numbers are negative, then the requirements for greater are
|
||||
-- reversed
|
||||
if (not equal) and (big1.sign == "-") and (big2.sign == "-") then
|
||||
greater = not greater
|
||||
end
|
||||
|
||||
return (((comparison == "<") or (comparison == "lt"))
|
||||
and ((not greater) and (not equal)) and true)
|
||||
or (((comparison == ">") or (comparison == "gt"))
|
||||
and ((greater) and (not equal)) and true)
|
||||
or (((comparison == "==") or (comparison == "eq"))
|
||||
and (equal) and true)
|
||||
or (((comparison == ">=") or (comparison == "ge"))
|
||||
and (equal or greater) and true)
|
||||
or (((comparison == "<=") or (comparison == "le"))
|
||||
and (equal or not greater) and true)
|
||||
or (((comparison == "~=") or (comparison == "!=") or (comparison == "ne"))
|
||||
and (not equal) and true)
|
||||
or false
|
||||
end
|
||||
|
||||
-- BACKEND: Add big1 and big2, ignoring signs
|
||||
function bigint.add_raw(big1, big2)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
|
||||
local result = bigint.new()
|
||||
local max_digits = 0
|
||||
local carry = 0
|
||||
|
||||
if (#big1.digits >= #big2.digits) then
|
||||
max_digits = #big1.digits
|
||||
else
|
||||
max_digits = #big2.digits
|
||||
end
|
||||
|
||||
-- Walk backwards right to left, like in long addition
|
||||
for digit = 0, max_digits - 1 do
|
||||
local sum = (big1.digits[#big1.digits - digit] or 0)
|
||||
+ (big2.digits[#big2.digits - digit] or 0)
|
||||
+ carry
|
||||
|
||||
if (sum >= 10) then
|
||||
carry = 1
|
||||
sum = sum - 10
|
||||
else
|
||||
carry = 0
|
||||
end
|
||||
|
||||
result.digits[max_digits - digit] = sum
|
||||
end
|
||||
|
||||
-- Leftover carry in cases when #big1.digits == #big2.digits and sum > 10, ex. 7 + 9
|
||||
if (carry == 1) then
|
||||
table.insert(result.digits, 1, 1)
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
end
|
||||
|
||||
-- BACKEND: Subtract big2 from big1, ignoring signs
|
||||
function bigint.subtract_raw(big1, big2)
|
||||
-- Type checking is done by bigint.compare
|
||||
assert(bigint.compare(bigint.abs(big1), bigint.abs(big2), ">="),
|
||||
"Size of " .. bigint.unserialize(big1, "string") .. " is less than "
|
||||
.. bigint.unserialize(big2, "string"))
|
||||
|
||||
local result = big1:clone()
|
||||
local max_digits = #big1.digits
|
||||
local borrow = 0
|
||||
|
||||
-- Logic mostly copied from bigint.add_raw ---------------------------------
|
||||
-- Walk backwards right to left, like in long subtraction
|
||||
for digit = 0, max_digits - 1 do
|
||||
local diff = (big1.digits[#big1.digits - digit] or 0)
|
||||
- (big2.digits[#big2.digits - digit] or 0)
|
||||
- borrow
|
||||
|
||||
if (diff < 0) then
|
||||
borrow = 1
|
||||
diff = diff + 10
|
||||
else
|
||||
borrow = 0
|
||||
end
|
||||
|
||||
result.digits[max_digits - digit] = diff
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- Strip leading zeroes if any, but not if 0 is the only digit
|
||||
while (#result.digits > 1) and (result.digits[1] == 0) do
|
||||
table.remove(result.digits, 1)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- FRONTEND: Addition and subtraction operations, accounting for signs
|
||||
function bigint.add(big1, big2)
|
||||
-- Type checking is done by bigint.compare
|
||||
|
||||
local result
|
||||
|
||||
-- If adding numbers of different sign, subtract the smaller sized one from
|
||||
-- the bigger sized one and take the sign of the bigger sized one
|
||||
if (big1.sign ~= big2.sign) then
|
||||
if (bigint.compare(bigint.abs(big1), bigint.abs(big2), ">")) then
|
||||
result = bigint.subtract_raw(big1, big2)
|
||||
result.sign = big1.sign
|
||||
else
|
||||
result = bigint.subtract_raw(big2, big1)
|
||||
result.sign = big2.sign
|
||||
end
|
||||
|
||||
elseif (big1.sign == "+") and (big2.sign == "+") then
|
||||
result = bigint.add_raw(big1, big2)
|
||||
|
||||
elseif (big1.sign == "-") and (big2.sign == "-") then
|
||||
result = bigint.add_raw(big1, big2)
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
function bigint.subtract(big1, big2)
|
||||
-- Type checking is done by bigint.compare in bigint.add
|
||||
-- Subtracting is like adding a negative
|
||||
local big2_local = big2:clone()
|
||||
if (big2.sign == "+") then
|
||||
big2_local.sign = "-"
|
||||
else
|
||||
big2_local.sign = "+"
|
||||
end
|
||||
return bigint.add(big1, big2_local)
|
||||
end
|
||||
|
||||
-- BACKEND: Multiply a big by a single digit big, ignoring signs
|
||||
function bigint.multiply_single(big1, big2)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
assert(#big2.digits == 1, bigint.unserialize(big2, "string")
|
||||
.. " has more than one digit")
|
||||
|
||||
local result = bigint.new()
|
||||
local carry = 0
|
||||
|
||||
-- Logic mostly copied from bigint.add_raw ---------------------------------
|
||||
-- Walk backwards right to left, like in long multiplication
|
||||
for digit = 0, #big1.digits - 1 do
|
||||
local this_digit = big1.digits[#big1.digits - digit]
|
||||
* big2.digits[1]
|
||||
+ carry
|
||||
|
||||
if (this_digit >= 10) then
|
||||
carry = math.floor(this_digit / 10)
|
||||
this_digit = this_digit - (carry * 10)
|
||||
else
|
||||
carry = 0
|
||||
end
|
||||
|
||||
result.digits[#big1.digits - digit] = this_digit
|
||||
end
|
||||
|
||||
-- Leftover carry in cases when big1.digits[1] * big2.digits[1] > 0
|
||||
if (carry > 0) then
|
||||
table.insert(result.digits, 1, carry)
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- FRONTEND: Multiply two bigs, accounting for signs
|
||||
function bigint.multiply(big1, big2)
|
||||
-- Type checking done by bigint.multiply_single
|
||||
|
||||
local result = bigint.new(0)
|
||||
local larger, smaller -- Larger and smaller in terms of digits, not size
|
||||
|
||||
if (bigint.unserialize(big1) == 0) or (bigint.unserialize(big2) == 0) then
|
||||
return result
|
||||
end
|
||||
|
||||
if (#big1.digits >= #big2.digits) then
|
||||
larger = big1
|
||||
smaller = big2
|
||||
else
|
||||
larger = big2
|
||||
smaller = big1
|
||||
end
|
||||
|
||||
-- Walk backwards right to left, like in long multiplication
|
||||
for digit = 0, #smaller.digits - 1 do
|
||||
-- Sorry for going over column 80! There's lots of big names here
|
||||
local this_digit_product = bigint.multiply_single(larger,
|
||||
bigint.new(smaller.digits[#smaller.digits - digit]))
|
||||
|
||||
-- "Placeholding zeroes"
|
||||
if (digit > 0) then
|
||||
for placeholder = 1, digit do
|
||||
table.insert(this_digit_product.digits, 0)
|
||||
end
|
||||
end
|
||||
|
||||
result = bigint.add(result, this_digit_product)
|
||||
end
|
||||
|
||||
if (larger.sign == smaller.sign) then
|
||||
result.sign = "+"
|
||||
else
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- Raise a big to a positive integer or big power (TODO: negative integer power)
|
||||
function bigint.exponentiate(big, power)
|
||||
-- Type checking for big done by bigint.multiply
|
||||
assert(bigint.compare(power, bigint.new(0), ">="),
|
||||
" negative powers are not supported")
|
||||
local exp = power:clone()
|
||||
|
||||
if (bigint.compare(exp, bigint.new(0), "==")) then
|
||||
return bigint.new(1)
|
||||
elseif (bigint.compare(exp, bigint.new(1), "==")) then
|
||||
return big:clone()
|
||||
else
|
||||
local result = bigint.new(1)
|
||||
local base = big:clone()
|
||||
|
||||
while (true) do
|
||||
if (bigint.compare(
|
||||
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
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- BACKEND: Divide two bigs (decimals not supported), returning big result and
|
||||
-- big remainder
|
||||
-- WARNING: Only supports positive integers
|
||||
function bigint.divide_raw(big1, big2)
|
||||
-- Type checking done by bigint.compare
|
||||
if (bigint.compare(big1, big2, "==")) then
|
||||
return bigint.new(1), bigint.new(0)
|
||||
elseif (bigint.compare(big1, big2, "<")) then
|
||||
return bigint.new(0), big1:clone()
|
||||
else
|
||||
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
|
||||
assert(big1.sign == "+", "error: big1 is not positive")
|
||||
assert(big2.sign == "+", "error: big2 is not positive")
|
||||
|
||||
local result = bigint.new()
|
||||
|
||||
local dividend = bigint.new() -- Dividend of a single operation
|
||||
|
||||
local neg_zero = bigint.new(0)
|
||||
neg_zero.sign = "-"
|
||||
|
||||
for i = 1, #big1.digits do
|
||||
-- Fixes a negative zero bug
|
||||
if (#dividend.digits ~= 0) and (bigint.compare(dividend, neg_zero, "==")) then
|
||||
dividend = bigint.new()
|
||||
end
|
||||
|
||||
table.insert(dividend.digits, big1.digits[i])
|
||||
|
||||
local factor = bigint.new(0)
|
||||
while bigint.compare(dividend, big2, ">=") do
|
||||
dividend = bigint.subtract(dividend, big2)
|
||||
factor = bigint.add(factor, bigint.new(1))
|
||||
end
|
||||
|
||||
for i = 0, #factor.digits - 1 do
|
||||
result.digits[#result.digits + 1 - i] = factor.digits[i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove leading zeros from result
|
||||
while (result.digits[1] == 0) do
|
||||
table.remove(result.digits, 1)
|
||||
end
|
||||
|
||||
return result, dividend
|
||||
end
|
||||
end
|
||||
|
||||
-- FRONTEND: Divide two bigs (decimals not supported), returning big result and
|
||||
-- big remainder, accounting for signs
|
||||
function bigint.divide(big1, big2)
|
||||
local result, remainder = bigint.divide_raw(bigint.abs(big1),
|
||||
bigint.abs(big2))
|
||||
if (big1.sign == big2.sign) then
|
||||
result.sign = "+"
|
||||
else
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result, remainder
|
||||
end
|
||||
|
||||
-- FRONTEND: Return only the remainder from bigint.divide
|
||||
function bigint.modulus(big1, big2)
|
||||
local result, remainder = bigint.divide(big1, big2)
|
||||
|
||||
-- Remainder will always have the same sign as the dividend per C standard
|
||||
-- https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation
|
||||
remainder.sign = big1.sign
|
||||
return remainder
|
||||
end
|
||||
|
||||
return bigint
|
||||
3340
libs/bigint/named-powers-of-ten.lua
Normal file
2
load/bigint.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
bigint = require "libs.bigint.bigint"
|
||||
number_names = require "libs.bigint.named-powers-of-ten"
|
||||
@@ -61,6 +61,11 @@ blocks = {
|
||||
F = love.graphics.newImage("res/img/gem9.png"),
|
||||
A = love.graphics.newImage("res/img/gem9.png"),
|
||||
X = love.graphics.newImage("res/img/gem9.png"),
|
||||
},
|
||||
["square"] = {
|
||||
F = love.graphics.newImage("res/img/squares.png"),
|
||||
Y = love.graphics.newImage("res/img/squareg.png"),
|
||||
X = love.graphics.newImage("res/img/squares.png"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
local binser = require 'libs.binser'
|
||||
|
||||
function loadSave()
|
||||
config = loadFromFile('config.sav')
|
||||
highscores = loadFromFile('highscores.sav')
|
||||
local info = love.filesystem.getInfo(love.filesystem.getSaveDirectory())
|
||||
if not info or info.type ~= "directory" then
|
||||
love.filesystem.remove(love.filesystem.getSaveDirectory())
|
||||
love.filesystem.createDirectory(love.filesystem.getSaveDirectory())
|
||||
end
|
||||
config = loadFromFile(
|
||||
love.filesystem.getSaveDirectory() .. '/config.sav'
|
||||
)
|
||||
highscores = loadFromFile(
|
||||
love.filesystem.getSaveDirectory() .. '/highscores.sav'
|
||||
)
|
||||
end
|
||||
|
||||
function loadFromFile(filename)
|
||||
@@ -13,12 +22,40 @@ function loadFromFile(filename)
|
||||
return save_data[1]
|
||||
end
|
||||
|
||||
function initConfig()
|
||||
if not config.das then config.das = 10 end
|
||||
if not config.arr then config.arr = 2 end
|
||||
if not config.dcd then config.dcd = 0 end
|
||||
if not config.sfx_volume then config.sfx_volume = 0.5 end
|
||||
if not config.bgm_volume then config.bgm_volume = 0.5 end
|
||||
|
||||
if config.fullscreen == nil then config.fullscreen = false end
|
||||
if config.secret == nil then config.secret = false end
|
||||
|
||||
if not config.gamesettings then config.gamesettings = {} end
|
||||
for _, option in ipairs(GameConfigScene.options) do
|
||||
if not config.gamesettings[option[1]] then
|
||||
config.gamesettings[option[1]] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if not config.input then
|
||||
scene = InputConfigScene()
|
||||
else
|
||||
if config.current_mode then current_mode = config.current_mode end
|
||||
if config.current_ruleset then current_ruleset = config.current_ruleset end
|
||||
scene = TitleScene()
|
||||
end
|
||||
end
|
||||
|
||||
function saveConfig()
|
||||
binser.writeFile('config.sav', config)
|
||||
binser.writeFile(
|
||||
love.filesystem.getSaveDirectory() .. '/config.sav', config
|
||||
)
|
||||
end
|
||||
|
||||
function saveHighscores()
|
||||
binser.writeFile('highscores.sav', highscores)
|
||||
binser.writeFile(
|
||||
love.filesystem.getSaveDirectory() .. '/highscores.sav', highscores
|
||||
)
|
||||
end
|
||||
|
||||
1
load/version.lua
Normal file
@@ -0,0 +1 @@
|
||||
version = "v0.3-beta5.2"
|
||||
209
main.lua
@@ -7,42 +7,32 @@ function love.load()
|
||||
require "load.sounds"
|
||||
require "load.bgm"
|
||||
require "load.save"
|
||||
require "load.bigint"
|
||||
require "load.version"
|
||||
loadSave()
|
||||
require "scene"
|
||||
|
||||
--config["side_next"] = false
|
||||
--config["reverse_rotate"] = true
|
||||
config["fullscreen"] = false
|
||||
--config["das_last_key"] = false
|
||||
--config["fullscreen"] = false
|
||||
|
||||
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
||||
|
||||
-- used for screenshots
|
||||
GLOBAL_CANVAS = love.graphics.newCanvas()
|
||||
|
||||
if not config.das then config.das = 10 end
|
||||
if not config.arr then config.arr = 2 end
|
||||
if not config.sfx_volume then config.sfx_volume = 0.5 end
|
||||
if not config.bgm_volume then config.bgm_volume = 0.5 end
|
||||
|
||||
if config.secret == nil then config.secret = false
|
||||
elseif config.secret == true then playSE("welcome") end
|
||||
-- init config
|
||||
initConfig()
|
||||
|
||||
if not config.gamesettings then
|
||||
config.gamesettings = {}
|
||||
config["das_last_key"] = false
|
||||
else
|
||||
config["das_last_key"] = config.gamesettings.das_last_key == 2
|
||||
end
|
||||
for _, option in ipairs(GameConfigScene.options) do
|
||||
if not config.gamesettings[option[1]] then
|
||||
config.gamesettings[option[1]] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if not config.input then
|
||||
scene = InputConfigScene()
|
||||
else
|
||||
if config.current_mode then current_mode = config.current_mode end
|
||||
if config.current_ruleset then current_ruleset = config.current_ruleset end
|
||||
scene = TitleScene()
|
||||
end
|
||||
love.window.setFullscreen(config["fullscreen"])
|
||||
if config.secret then playSE("welcome") end
|
||||
|
||||
-- import custom modules
|
||||
initModules()
|
||||
end
|
||||
|
||||
function initModules()
|
||||
game_modes = {}
|
||||
mode_list = love.filesystem.getDirectoryItems("tetris/modes")
|
||||
for i=1,#mode_list do
|
||||
@@ -63,48 +53,12 @@ function love.load()
|
||||
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||
table.sort(rulesets, function(a,b)
|
||||
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||
|
||||
end
|
||||
|
||||
local TARGET_FPS = 60
|
||||
local SAMPLE_SIZE = 60
|
||||
|
||||
local rolling_samples = {}
|
||||
local rolling_total = 0
|
||||
local average_n = 0
|
||||
local frame = 0
|
||||
|
||||
function getSmoothedDt(dt)
|
||||
rolling_total = rolling_total + dt
|
||||
frame = frame + 1
|
||||
if frame > SAMPLE_SIZE then frame = frame - SAMPLE_SIZE end
|
||||
if average_n == SAMPLE_SIZE then
|
||||
rolling_total = rolling_total - rolling_samples[frame]
|
||||
else
|
||||
average_n = average_n + 1
|
||||
end
|
||||
rolling_samples[frame] = dt
|
||||
return rolling_total / average_n
|
||||
end
|
||||
|
||||
local update_time = 0.52
|
||||
|
||||
function love.update(dt)
|
||||
processBGMFadeout(dt)
|
||||
local old_update_time = update_time
|
||||
update_time = update_time + getSmoothedDt(dt) * TARGET_FPS
|
||||
updates = 0
|
||||
while (update_time >= 1.02) do
|
||||
scene:update()
|
||||
updates = updates + 1
|
||||
update_time = update_time - 1
|
||||
end
|
||||
if math.abs(update_time - old_update_time) < 0.02 then
|
||||
update_time = old_update_time
|
||||
end
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
love.graphics.setCanvas(GLOBAL_CANVAS)
|
||||
love.graphics.clear()
|
||||
|
||||
love.graphics.push()
|
||||
|
||||
-- get offset matrix
|
||||
@@ -117,15 +71,20 @@ function love.draw()
|
||||
(height - scale_factor * 480) / 2
|
||||
)
|
||||
love.graphics.scale(scale_factor)
|
||||
|
||||
|
||||
scene:render()
|
||||
love.graphics.pop()
|
||||
|
||||
love.graphics.setCanvas()
|
||||
love.graphics.setColor(1,1,1,1)
|
||||
love.graphics.draw(GLOBAL_CANVAS)
|
||||
end
|
||||
|
||||
function love.keypressed(key, scancode)
|
||||
-- global hotkeys
|
||||
if scancode == "f4" then
|
||||
config["fullscreen"] = not config["fullscreen"]
|
||||
saveConfig()
|
||||
love.window.setFullscreen(config["fullscreen"])
|
||||
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
|
||||
scene = InputConfigScene()
|
||||
@@ -137,6 +96,16 @@ function love.keypressed(key, scancode)
|
||||
scene.restart_message = true
|
||||
if config.secret then playSE("mode_decide")
|
||||
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")
|
||||
local info = love.filesystem.getInfo("ss")
|
||||
if not info or info.type ~= "directory" then
|
||||
love.filesystem.remove("ss")
|
||||
love.filesystem.createDirectory("ss")
|
||||
end
|
||||
print("Saving screenshot as "..ss_name)
|
||||
GLOBAL_CANVAS:newImageData():encode("png", ss_name)
|
||||
-- function keys are reserved
|
||||
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
|
||||
return
|
||||
@@ -207,13 +176,13 @@ function love.joystickaxis(joystick, axis, value)
|
||||
config.input.joysticks[joystick:getName()].axes and
|
||||
config.input.joysticks[joystick:getName()].axes[axis]
|
||||
then
|
||||
if math.abs(value) >= 0.5 then
|
||||
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"]
|
||||
if math.abs(value) >= 1 then
|
||||
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
|
||||
end
|
||||
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
|
||||
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
|
||||
end
|
||||
if math.abs(value) >= 0.5 then
|
||||
if math.abs(value) >= 1 then
|
||||
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
|
||||
else
|
||||
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
|
||||
@@ -221,6 +190,14 @@ function love.joystickaxis(joystick, axis, value)
|
||||
end
|
||||
end
|
||||
|
||||
local last_hat_direction = ""
|
||||
local directions = {
|
||||
["u"] = "up",
|
||||
["d"] = "down",
|
||||
["l"] = "left",
|
||||
["r"] = "right",
|
||||
}
|
||||
|
||||
function love.joystickhat(joystick, hat, direction)
|
||||
local input_pressed = nil
|
||||
local has_hat = false
|
||||
@@ -237,17 +214,47 @@ function love.joystickhat(joystick, hat, direction)
|
||||
has_hat = true
|
||||
end
|
||||
if input_pressed then
|
||||
scene:onInputPress({input=input_pressed, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||
for i = 1, #direction do
|
||||
local char = direction:sub(i, i)
|
||||
local _, count = last_hat_direction:gsub(char, char)
|
||||
if count == 0 then
|
||||
scene:onInputPress({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
|
||||
end
|
||||
end
|
||||
for i = 1, #last_hat_direction do
|
||||
local char = last_hat_direction:sub(i, i)
|
||||
local _, count = direction:gsub(char, char)
|
||||
if count == 0 then
|
||||
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
|
||||
end
|
||||
end
|
||||
last_hat_direction = direction
|
||||
elseif has_hat then
|
||||
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
|
||||
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||
end
|
||||
last_hat_direction = ""
|
||||
elseif direction ~= "c" then
|
||||
scene:onInputPress({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||
for i = 1, #direction do
|
||||
local char = direction:sub(i, i)
|
||||
local _, count = last_hat_direction:gsub(char, char)
|
||||
if count == 0 then
|
||||
scene:onInputPress({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
|
||||
end
|
||||
end
|
||||
for i = 1, #last_hat_direction do
|
||||
local char = last_hat_direction:sub(i, i)
|
||||
local _, count = direction:gsub(char, char)
|
||||
if count == 0 then
|
||||
scene:onInputRelease({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
|
||||
end
|
||||
end
|
||||
last_hat_direction = direction
|
||||
else
|
||||
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
|
||||
scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||
end
|
||||
last_hat_direction = ""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -258,3 +265,61 @@ function love.focus(f)
|
||||
pauseBGM()
|
||||
end
|
||||
end
|
||||
|
||||
function love.resize(w, h)
|
||||
GLOBAL_CANVAS:release()
|
||||
GLOBAL_CANVAS = love.graphics.newCanvas(w, h)
|
||||
end
|
||||
|
||||
local TARGET_FPS = 60
|
||||
|
||||
function love.run()
|
||||
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
|
||||
|
||||
if love.timer then love.timer.step() end
|
||||
|
||||
local dt = 0
|
||||
|
||||
local last_time = love.timer.getTime()
|
||||
local time_accumulator = 0
|
||||
return function()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for name, a,b,c,d,e,f in love.event.poll() do
|
||||
if name == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
return a or 0
|
||||
end
|
||||
end
|
||||
love.handlers[name](a,b,c,d,e,f)
|
||||
end
|
||||
end
|
||||
|
||||
if love.timer then
|
||||
processBGMFadeout(love.timer.step())
|
||||
end
|
||||
|
||||
if scene and scene.update and love.timer then
|
||||
scene:update()
|
||||
|
||||
local frame_duration = 1.0 / TARGET_FPS
|
||||
if time_accumulator < frame_duration then
|
||||
if love.graphics and love.graphics.isActive() and love.draw then
|
||||
love.graphics.origin()
|
||||
love.graphics.clear(love.graphics.getBackgroundColor())
|
||||
love.draw()
|
||||
love.graphics.present()
|
||||
end
|
||||
local end_time = last_time + frame_duration
|
||||
local time = love.timer.getTime()
|
||||
while time < end_time do
|
||||
love.timer.sleep(0.001)
|
||||
time = love.timer.getTime()
|
||||
end
|
||||
time_accumulator = time_accumulator + time - last_time
|
||||
end
|
||||
time_accumulator = time_accumulator - frame_duration
|
||||
end
|
||||
last_time = love.timer.getTime()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 229 B After Width: | Height: | Size: 153 B |
BIN
res/img/cambridge_icon.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/img/squareg.png
Normal file
|
After Width: | Height: | Size: 708 B |
BIN
res/img/squares.png
Normal file
|
After Width: | Height: | Size: 639 B |
@@ -11,6 +11,8 @@ function Scene:onInputRelease() end
|
||||
ExitScene = require "scene.exit"
|
||||
GameScene = require "scene.game"
|
||||
ModeSelectScene = require "scene.mode_select"
|
||||
KeyConfigScene = require "scene.key_config"
|
||||
StickConfigScene = require "scene.stick_config"
|
||||
InputConfigScene = require "scene.input_config"
|
||||
GameConfigScene = require "scene.game_config"
|
||||
TuningScene = require "scene.tuning"
|
||||
|
||||
@@ -8,7 +8,9 @@ function CreditsScene:new()
|
||||
end
|
||||
|
||||
function CreditsScene:update()
|
||||
self.frames = self.frames + 1
|
||||
if love.window.hasFocus() then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
if self.frames >= 4200 then
|
||||
playSE("mode_decide")
|
||||
scene = TitleScene()
|
||||
@@ -28,26 +30,35 @@ function CreditsScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2)
|
||||
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1500 - self.frames / 2, 240))
|
||||
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1770 - self.frames / 2, 240))
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("Game Developers", 320, 550 - self.frames / 2)
|
||||
love.graphics.print("Project Heads", 320, 640 - self.frames / 2)
|
||||
love.graphics.print("Other Game Developers", 320, 730 - self.frames / 2)
|
||||
love.graphics.print("Special Thanks", 320, 900 - self.frames / 2)
|
||||
love.graphics.print("- SashLilac / SpinTriple", 320, math.max(2000 - self.frames / 2, 320))
|
||||
love.graphics.print("Special Thanks", 320, 950 - self.frames / 2)
|
||||
love.graphics.print("- Milla", 320, math.max(1850 - self.frames / 2, 320))
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - self.frames / 2)
|
||||
love.graphics.print("Mizu\nHailey", 320, 680 - self.frames / 2)
|
||||
love.graphics.print("Axel Fox - Multimino\nMine - Tetra Online\nDr Ocelot - Tetra Legends\nFelicity / nightmareci - Shiromino\n2Tie - TGMsim\nPhoenix Flare - Master of Blocks", 320, 770 - self.frames / 2)
|
||||
love.graphics.print(
|
||||
"Axel Fox - Multimino\nMine - Tetra Online\nDr Ocelot - Tetra Legends\n" ..
|
||||
"Felicity / nightmareci - Shiromino\n2Tie - TGMsim\nPhoenix Flare - Master of Blocks\n" ..
|
||||
"RayRay26 - Spirit Drop\nosk - TETR.IO\nMarkGamed7794 - Picoris 2",
|
||||
320, 770 - self.frames / 2
|
||||
)
|
||||
love.graphics.print(
|
||||
"RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" ..
|
||||
"Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" ..
|
||||
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\nMarkGamed7794\n" ..
|
||||
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nThe Absolute PLUS Discord\nTetra Legends Discord\n" ..
|
||||
"Tetra Online Discord\nMultimino Discord\nCambridge Discord\nAnd to you, the player!",
|
||||
320, 940 - self.frames / 2
|
||||
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\n" ..
|
||||
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nArchina\nOliver\ncolour_thief\n" ..
|
||||
"Caithness\nkdex\nzid\nsaphie\nSuper302\nAurora\nswitchpalacecorner\nKitaru\n" ..
|
||||
"JBroms\nMany more I definitely missed!\n" ..
|
||||
"The Absolute PLUS Discord\nTetra Legends Discord\nTetra Online Discord\n" ..
|
||||
"Multimino Discord\nHard Drop Discord\nCambridge Discord\n" ..
|
||||
"And to you, the player!",
|
||||
320, 990 - self.frames / 2
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ GameScene.title = "Game"
|
||||
|
||||
require 'load.save'
|
||||
|
||||
function GameScene:new(game_mode, ruleset)
|
||||
function GameScene:new(game_mode, ruleset, inputs)
|
||||
self.retry_mode = game_mode
|
||||
self.retry_ruleset = ruleset
|
||||
self.game = game_mode()
|
||||
self.ruleset = ruleset()
|
||||
self.secret_inputs = inputs
|
||||
self.game = game_mode(self.secret_inputs)
|
||||
self.ruleset = ruleset(self.game)
|
||||
self.game:initialize(self.ruleset)
|
||||
self.inputs = {
|
||||
left=false,
|
||||
@@ -49,11 +50,40 @@ function GameScene:render()
|
||||
)
|
||||
|
||||
-- game frame
|
||||
love.graphics.draw(misc_graphics["frame"], 48, 64)
|
||||
if self.game.grid.width == 10 and self.game.grid.height == 24 then
|
||||
love.graphics.draw(misc_graphics["frame"], 48, 64)
|
||||
end
|
||||
|
||||
love.graphics.setColor(0, 0, 0, 200)
|
||||
love.graphics.rectangle("fill", 64, 80, 160, 320)
|
||||
love.graphics.rectangle(
|
||||
"fill", 64, 80,
|
||||
16 * self.game.grid.width, 16 * (self.game.grid.height - 4)
|
||||
)
|
||||
|
||||
if self.game.grid.width ~= 10 or self.game.grid.height ~= 24 then
|
||||
love.graphics.setColor(174/255, 83/255, 76/255, 1)
|
||||
love.graphics.setLineWidth(8)
|
||||
love.graphics.line(
|
||||
60,76,
|
||||
68+16*self.game.grid.width,76,
|
||||
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
|
||||
60,84+16*(self.game.grid.height-4),
|
||||
60,76
|
||||
)
|
||||
love.graphics.setColor(203/255, 137/255, 111/255, 1)
|
||||
love.graphics.setLineWidth(4)
|
||||
love.graphics.line(
|
||||
60,76,
|
||||
68+16*self.game.grid.width,76,
|
||||
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
|
||||
60,84+16*(self.game.grid.height-4),
|
||||
60,76
|
||||
)
|
||||
love.graphics.setLineWidth(1)
|
||||
end
|
||||
|
||||
self.game:drawGrid()
|
||||
if self.game.lcd > 0 then self.game:drawLineClearAnimation() end
|
||||
self.game:drawPiece()
|
||||
self.game:drawNextQueue(self.ruleset)
|
||||
self.game:drawScoringInfo()
|
||||
@@ -75,21 +105,31 @@ function GameScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.paused then love.graphics.print("PAUSED!", 80, 100) end
|
||||
|
||||
if self.game.completed then
|
||||
self.game:onGameComplete()
|
||||
elseif self.game.game_over then
|
||||
self.game:onGameOver()
|
||||
end
|
||||
end
|
||||
|
||||
function GameScene:onInputPress(e)
|
||||
if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
|
||||
if (self.game.game_over or self.game.completed) and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
|
||||
highscore_entry = self.game:getHighscoreData()
|
||||
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
|
||||
submitHighscore(highscore_hash, highscore_entry)
|
||||
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) or ModeSelectScene()
|
||||
self.game:onExit()
|
||||
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
|
||||
elseif e.input == "retry" then
|
||||
scene = GameScene(self.retry_mode, self.retry_ruleset)
|
||||
switchBGM(nil)
|
||||
self.game:onExit()
|
||||
scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs)
|
||||
elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
|
||||
self.paused = not self.paused
|
||||
if self.paused then pauseBGM()
|
||||
else resumeBGM() end
|
||||
elseif e.input == "menu_back" then
|
||||
self.game:onExit()
|
||||
scene = ModeSelectScene()
|
||||
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
|
||||
self.inputs[e.input] = true
|
||||
|
||||
@@ -11,11 +11,13 @@ ConfigScene.options = {
|
||||
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
|
||||
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
|
||||
{"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
|
||||
{"spawn_positions", "Spawn Positions", false, {"Per ruleset", "In field", "Out of field"}},
|
||||
{"display_gamemode", "Display Gamemode", false, {"On", "Off"}},
|
||||
{"das_last_key", "DAS Switch", false, {"Default", "Instant"}},
|
||||
{"das_last_key", "DAS Last Key", false, {"Off", "On"}},
|
||||
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
|
||||
{"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}},
|
||||
{"diagonal_input", "Diagonal Input", false, {"On", "Off"}},
|
||||
{"buffer_lock", "Buffer Lock Inputs", false, {"On", "Off"}},
|
||||
{"sfx_volume", "SFX", true, "sfxSlider"},
|
||||
{"bgm_volume", "BGM", true, "bgmSlider"},
|
||||
}
|
||||
@@ -31,12 +33,11 @@ function ConfigScene:new()
|
||||
state = "Changing game settings",
|
||||
})
|
||||
|
||||
self.sfxSlider = newSlider(165, 375, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20})
|
||||
self.bgmSlider = newSlider(465, 375, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20})
|
||||
self.sfxSlider = newSlider(165, 400, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
|
||||
self.bgmSlider = newSlider(465, 400, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
|
||||
end
|
||||
|
||||
function ConfigScene:update()
|
||||
config["das_last_key"] = config.gamesettings.das_last_key == 2
|
||||
self.sfxSlider:update()
|
||||
self.bgmSlider:update()
|
||||
end
|
||||
@@ -55,9 +56,9 @@ function ConfigScene:render()
|
||||
--Lazy check to see if we're on the SFX or BGM slider. Probably will need to be rewritten if more options get added.
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
if not ConfigScene.options[self.highlight][3] then
|
||||
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22)
|
||||
love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
|
||||
else
|
||||
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 322, 215, 33)
|
||||
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 342, 215, 33)
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
@@ -74,8 +75,8 @@ function ConfigScene:render()
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 325)
|
||||
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 325)
|
||||
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 345)
|
||||
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 345)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75)
|
||||
self.sfxSlider:draw()
|
||||
@@ -101,7 +102,7 @@ function ConfigScene:onInputPress(e)
|
||||
else
|
||||
playSE("cursor")
|
||||
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
|
||||
elseif e.input == "right" or e.scancode == "right" then
|
||||
if not self.options[self.highlight][3] then
|
||||
@@ -111,7 +112,7 @@ function ConfigScene:onInputPress(e)
|
||||
else
|
||||
playSE("cursor")
|
||||
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
|
||||
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||
loadSave()
|
||||
|
||||
@@ -2,162 +2,65 @@ local ConfigScene = Scene:extend()
|
||||
|
||||
ConfigScene.title = "Input Config"
|
||||
|
||||
require 'load.save'
|
||||
|
||||
local configurable_inputs = {
|
||||
"menu_decide",
|
||||
"menu_back",
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"rotate_left",
|
||||
"rotate_left2",
|
||||
"rotate_right",
|
||||
"rotate_right2",
|
||||
"rotate_180",
|
||||
"hold",
|
||||
"retry",
|
||||
"pause",
|
||||
local menu_screens = {
|
||||
KeyConfigScene,
|
||||
StickConfigScene
|
||||
}
|
||||
|
||||
local function newSetInputs()
|
||||
local set_inputs = {}
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
set_inputs[input] = false
|
||||
end
|
||||
return set_inputs
|
||||
end
|
||||
|
||||
function ConfigScene:new()
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
self.axis_timer = 0
|
||||
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing input config",
|
||||
})
|
||||
self.menu_state = 1
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing input config",
|
||||
})
|
||||
end
|
||||
|
||||
function ConfigScene:update()
|
||||
end
|
||||
function ConfigScene:update() end
|
||||
|
||||
function ConfigScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds["input_config"],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||
if self.set_inputs[input] then
|
||||
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
|
||||
end
|
||||
end
|
||||
if self.input_state > table.getn(configurable_inputs) then
|
||||
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
|
||||
else
|
||||
love.graphics.print("press key or joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
|
||||
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
|
||||
end
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.print("INPUT CONFIG", 80, 40)
|
||||
|
||||
self.axis_timer = self.axis_timer + 1
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print("Which controls do you want to configure?", 80, 90)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
love.graphics.rectangle("fill", 75, 118 + 50 * self.menu_state, 200, 33)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
for i, screen in pairs(menu_screens) do
|
||||
love.graphics.printf(screen.title, 80, 120 + 50 * i, 200, "left")
|
||||
end
|
||||
end
|
||||
|
||||
local function addJoystick(input, name)
|
||||
if not input.joysticks then
|
||||
input.joysticks = {}
|
||||
end
|
||||
if not input.joysticks[name] then
|
||||
input.joysticks[name] = {}
|
||||
end
|
||||
function ConfigScene:changeOption(rel)
|
||||
local len = table.getn(menu_screens)
|
||||
self.menu_state = (self.menu_state + len + rel - 1) % len + 1
|
||||
end
|
||||
|
||||
function ConfigScene:onInputPress(e)
|
||||
if e.type == "key" then
|
||||
-- function keys, escape, and tab are reserved and can't be remapped
|
||||
if e.scancode == "escape" and config.input then
|
||||
-- cancel only if there was an input config already
|
||||
scene = SettingsScene()
|
||||
elseif self.input_state > table.getn(configurable_inputs) then
|
||||
if e.scancode == "return" then
|
||||
-- save new input, then load next scene
|
||||
config.input = self.new_input
|
||||
saveConfig()
|
||||
scene = TitleScene()
|
||||
elseif e.scancode == "delete" or e.scancode == "backspace" then
|
||||
-- retry
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
end
|
||||
elseif e.scancode == "tab" then
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
|
||||
self.input_state = self.input_state + 1
|
||||
elseif e.scancode ~= "escape" then
|
||||
-- all other keys can be configured
|
||||
if not self.new_input.keys then
|
||||
self.new_input.keys = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
|
||||
self.new_input.keys[e.scancode] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
elseif string.sub(e.type, 1, 3) == "joy" then
|
||||
if self.input_state <= table.getn(configurable_inputs) then
|
||||
if e.type == "joybutton" then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input.joysticks[e.name].buttons then
|
||||
self.new_input.joysticks[e.name].buttons = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jbtn " ..
|
||||
e.button ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input.joysticks[e.name].buttons[e.button] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
elseif e.type == "joyaxis" then
|
||||
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input.joysticks[e.name].axes then
|
||||
self.new_input.joysticks[e.name].axes = {}
|
||||
end
|
||||
if not self.new_input.joysticks[e.name].axes[e.axis] then
|
||||
self.new_input.joysticks[e.name].axes[e.axis] = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jaxis " ..
|
||||
(e.value >= 1 and "+" or "-") .. e.axis ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input.joysticks[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
self.last_axis = e.axis
|
||||
self.axis_timer = 0
|
||||
end
|
||||
elseif e.type == "joyhat" then
|
||||
if e.direction ~= "c" then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input.joysticks[e.name].hats then
|
||||
self.new_input.joysticks[e.name].hats = {}
|
||||
end
|
||||
if not self.new_input.joysticks[e.name].hats[e.hat] then
|
||||
self.new_input.joysticks[e.name].hats[e.hat] = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jhat " ..
|
||||
e.hat .. " " .. e.direction ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if e.input == "menu_decide" or e.scancode == "return" then
|
||||
playSE("main_decide")
|
||||
scene = menu_screens[self.menu_state]()
|
||||
elseif e.input == "up" or e.scancode == "up" then
|
||||
self:changeOption(-1)
|
||||
playSE("cursor")
|
||||
elseif e.input == "down" or e.scancode == "down" then
|
||||
self:changeOption(1)
|
||||
playSE("cursor")
|
||||
elseif config.input and (
|
||||
e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete"
|
||||
) then
|
||||
scene = SettingsScene()
|
||||
end
|
||||
end
|
||||
|
||||
return ConfigScene
|
||||
return ConfigScene
|
||||
100
scene/key_config.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
local KeyConfigScene = Scene:extend()
|
||||
|
||||
KeyConfigScene.title = "Key Config"
|
||||
|
||||
require 'load.save'
|
||||
|
||||
local configurable_inputs = {
|
||||
"menu_decide",
|
||||
"menu_back",
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"rotate_left",
|
||||
"rotate_left2",
|
||||
"rotate_right",
|
||||
"rotate_right2",
|
||||
"rotate_180",
|
||||
"hold",
|
||||
"retry",
|
||||
"pause",
|
||||
}
|
||||
|
||||
local function newSetInputs()
|
||||
local set_inputs = {}
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
set_inputs[input] = false
|
||||
end
|
||||
return set_inputs
|
||||
end
|
||||
|
||||
function KeyConfigScene:new()
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing key config",
|
||||
})
|
||||
end
|
||||
|
||||
function KeyConfigScene:update()
|
||||
end
|
||||
|
||||
function KeyConfigScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds["input_config"],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||
if self.set_inputs[input] then
|
||||
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
|
||||
end
|
||||
end
|
||||
if self.input_state > table.getn(configurable_inputs) then
|
||||
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
|
||||
else
|
||||
love.graphics.print("press key input for " .. configurable_inputs[self.input_state] .. ", tab to skip, escape to cancel", 0, 0)
|
||||
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
|
||||
end
|
||||
end
|
||||
|
||||
function KeyConfigScene:onInputPress(e)
|
||||
if e.type == "key" then
|
||||
-- function keys, escape, and tab are reserved and can't be remapped
|
||||
if e.scancode == "escape" then
|
||||
scene = InputConfigScene()
|
||||
elseif self.input_state > table.getn(configurable_inputs) then
|
||||
if e.scancode == "return" then
|
||||
-- save new input, then load next scene
|
||||
local had_config = config.input ~= nil
|
||||
if not config.input then config.input = {} end
|
||||
config.input.keys = self.new_input
|
||||
saveConfig()
|
||||
scene = had_config and InputConfigScene() or TitleScene()
|
||||
elseif e.scancode == "delete" or e.scancode == "backspace" then
|
||||
-- retry
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
end
|
||||
elseif e.scancode == "tab" then
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
|
||||
self.input_state = self.input_state + 1
|
||||
elseif e.scancode ~= "escape" then
|
||||
-- all other keys can be configured
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
|
||||
self.new_input[e.scancode] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return KeyConfigScene
|
||||
@@ -11,6 +11,14 @@ function ModeSelectScene:new()
|
||||
ruleset = current_ruleset,
|
||||
select = "mode",
|
||||
}
|
||||
self.secret_inputs = {
|
||||
rotate_left = false,
|
||||
rotate_left2 = false,
|
||||
rotate_right = false,
|
||||
rotate_right2 = false,
|
||||
rotate_180 = false,
|
||||
hold = false,
|
||||
}
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Choosing a mode",
|
||||
@@ -67,7 +75,7 @@ function ModeSelectScene:onInputPress(e)
|
||||
config.current_ruleset = current_ruleset
|
||||
playSE("mode_decide")
|
||||
saveConfig()
|
||||
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset])
|
||||
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs)
|
||||
elseif e.input == "up" or e.scancode == "up" then
|
||||
self:changeOption(-1)
|
||||
playSE("cursor")
|
||||
@@ -79,6 +87,14 @@ function ModeSelectScene:onInputPress(e)
|
||||
playSE("cursor_lr")
|
||||
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||
scene = TitleScene()
|
||||
elseif e.input then
|
||||
self.secret_inputs[e.input] = true
|
||||
end
|
||||
end
|
||||
|
||||
function ModeSelectScene:onInputRelease(e)
|
||||
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then
|
||||
self.secret_inputs[e.input] = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
152
scene/stick_config.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
local StickConfigScene = Scene:extend()
|
||||
|
||||
StickConfigScene.title = "Joystick Config"
|
||||
|
||||
require 'load.save'
|
||||
|
||||
local configurable_inputs = {
|
||||
"menu_decide",
|
||||
"menu_back",
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"rotate_left",
|
||||
"rotate_left2",
|
||||
"rotate_right",
|
||||
"rotate_right2",
|
||||
"rotate_180",
|
||||
"hold",
|
||||
"retry",
|
||||
"pause",
|
||||
}
|
||||
|
||||
local function newSetInputs()
|
||||
local set_inputs = {}
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
set_inputs[input] = false
|
||||
end
|
||||
return set_inputs
|
||||
end
|
||||
|
||||
function StickConfigScene:new()
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
self.axis_timer = 0
|
||||
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing joystick config",
|
||||
})
|
||||
end
|
||||
|
||||
function StickConfigScene:update()
|
||||
end
|
||||
|
||||
function StickConfigScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds["input_config"],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for i, input in ipairs(configurable_inputs) do
|
||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||
if self.set_inputs[input] then
|
||||
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
|
||||
end
|
||||
end
|
||||
if self.input_state > table.getn(configurable_inputs) then
|
||||
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
|
||||
else
|
||||
love.graphics.print("press joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip, escape to cancel", 0, 0)
|
||||
end
|
||||
|
||||
self.axis_timer = self.axis_timer + 1
|
||||
end
|
||||
|
||||
local function addJoystick(input, name)
|
||||
if not input[name] then
|
||||
input[name] = {}
|
||||
end
|
||||
end
|
||||
|
||||
function StickConfigScene:onInputPress(e)
|
||||
if e.type == "key" then
|
||||
-- function keys, escape, and tab are reserved and can't be remapped
|
||||
if e.scancode == "escape" then
|
||||
scene = InputConfigScene()
|
||||
elseif self.input_state > table.getn(configurable_inputs) then
|
||||
if e.scancode == "return" then
|
||||
-- save new input, then load next scene
|
||||
local had_config = config.input ~= nil
|
||||
if not config.input then config.input = {} end
|
||||
config.input.joysticks = self.new_input
|
||||
saveConfig()
|
||||
scene = had_config and InputConfigScene() or TitleScene()
|
||||
elseif e.scancode == "delete" or e.scancode == "backspace" then
|
||||
-- retry
|
||||
self.input_state = 1
|
||||
self.set_inputs = newSetInputs()
|
||||
self.new_input = {}
|
||||
end
|
||||
elseif e.scancode == "tab" then
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
elseif string.sub(e.type, 1, 3) == "joy" then
|
||||
if self.input_state <= table.getn(configurable_inputs) then
|
||||
if e.type == "joybutton" then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input[e.name].buttons then
|
||||
self.new_input[e.name].buttons = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jbtn " ..
|
||||
e.button ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input[e.name].buttons[e.button] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
elseif e.type == "joyaxis" then
|
||||
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input[e.name].axes then
|
||||
self.new_input[e.name].axes = {}
|
||||
end
|
||||
if not self.new_input[e.name].axes[e.axis] then
|
||||
self.new_input[e.name].axes[e.axis] = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jaxis " ..
|
||||
(e.value >= 1 and "+" or "-") .. e.axis ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
self.last_axis = e.axis
|
||||
self.axis_timer = 0
|
||||
end
|
||||
elseif e.type == "joyhat" then
|
||||
if e.direction ~= "c" then
|
||||
addJoystick(self.new_input, e.name)
|
||||
if not self.new_input[e.name].hats then
|
||||
self.new_input[e.name].hats = {}
|
||||
end
|
||||
if not self.new_input[e.name].hats[e.hat] then
|
||||
self.new_input[e.name].hats[e.hat] = {}
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jhat " ..
|
||||
e.hat .. " " .. e.direction ..
|
||||
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||
self.new_input[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return StickConfigScene
|
||||
@@ -72,6 +72,7 @@ function TitleScene:render()
|
||||
)
|
||||
love.graphics.print("Happy Holidays!", 320, -100 + self.y_offset)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.print(self.restart_message and "Restart Cambridge..." or "", 0, 0)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
@@ -82,6 +83,7 @@ function TitleScene:render()
|
||||
love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left")
|
||||
end
|
||||
|
||||
love.graphics.printf(version, 0, 460, love.graphics.getWidth() - 5, "right")
|
||||
end
|
||||
|
||||
function TitleScene:changeOption(rel)
|
||||
|
||||
@@ -9,6 +9,7 @@ TuningScene.options = {
|
||||
-- Serves as a reference for the options available in the menu. Format: {name in config, name as displayed if applicable, slider name}
|
||||
{"das", "DAS", "dasSlider"},
|
||||
{"arr", "ARR", "arrSlider"},
|
||||
{"dcd", "DCD", "dcdSlider"},
|
||||
}
|
||||
|
||||
local optioncount = #TuningScene.options
|
||||
@@ -20,13 +21,15 @@ function TuningScene:new()
|
||||
})
|
||||
self.highlight = 1
|
||||
|
||||
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20})
|
||||
self.arrSlider = newSlider(290, 325, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20})
|
||||
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
self.arrSlider = newSlider(290, 300, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
self.dcdSlider = newSlider(290, 375, 400, config.dcd, 0, 6, function(v) config.dcd = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
end
|
||||
|
||||
function TuningScene:update()
|
||||
self.dasSlider:update()
|
||||
self.arrSlider:update()
|
||||
self.arrSlider:update()
|
||||
self.dcdSlider:update()
|
||||
end
|
||||
|
||||
function TuningScene:render()
|
||||
@@ -38,7 +41,7 @@ function TuningScene:render()
|
||||
)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
love.graphics.rectangle("fill", 75, 73 + self.highlight * 100, 400, 33)
|
||||
love.graphics.rectangle("fill", 75, 98 + self.highlight * 75, 400, 33)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -50,11 +53,13 @@ function TuningScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("Delayed Auto-Shift (DAS): " .. math.floor(self.dasSlider:getValue()) .. "F", 80, 175)
|
||||
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 275)
|
||||
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 250)
|
||||
love.graphics.print("DAS Cut Delay (DCD): " .. math.floor(self.dcdSlider:getValue()) .. "F", 80, 325)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75)
|
||||
self.dasSlider:draw()
|
||||
self.arrSlider:draw()
|
||||
self.arrSlider:draw()
|
||||
self.dcdSlider:draw()
|
||||
end
|
||||
|
||||
function TuningScene:onInputPress(e)
|
||||
|
||||
@@ -6,13 +6,15 @@ local empty = { skin = "", colour = "" }
|
||||
local oob = { skin = "", colour = "" }
|
||||
local block = { skin = "2tie", colour = "A" }
|
||||
|
||||
function Grid:new()
|
||||
function Grid:new(width, height)
|
||||
self.grid = {}
|
||||
self.grid_age = {}
|
||||
for y = 1, 24 do
|
||||
self.width = width
|
||||
self.height = height
|
||||
for y = 1, self.height do
|
||||
self.grid[y] = {}
|
||||
self.grid_age[y] = {}
|
||||
for x = 1, 10 do
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
@@ -20,8 +22,8 @@ function Grid:new()
|
||||
end
|
||||
|
||||
function Grid:clear()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
@@ -29,7 +31,7 @@ function Grid:clear()
|
||||
end
|
||||
|
||||
function Grid:getCell(x, y)
|
||||
if x < 1 or x > 10 or y > 24 then return oob
|
||||
if x < 1 or x > self.width or y > self.height then return oob
|
||||
elseif y < 1 then return empty
|
||||
else return self.grid[y][x]
|
||||
end
|
||||
@@ -98,89 +100,84 @@ end
|
||||
|
||||
function Grid:getClearedRowCount()
|
||||
local count = 0
|
||||
for row = 1, 24 do
|
||||
local cleared_row_table = {}
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
count = count + 1
|
||||
table.insert(cleared_row_table, row)
|
||||
end
|
||||
end
|
||||
return count
|
||||
return count, cleared_row_table
|
||||
end
|
||||
|
||||
function Grid:markClearedRows()
|
||||
for row = 1, 24 do
|
||||
local block_table = {}
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
for x = 1, 10 do
|
||||
block_table[row] = {}
|
||||
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] = {
|
||||
skin = self.grid[row][x].skin,
|
||||
colour = "X"
|
||||
}
|
||||
--self.grid_age[row][x] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
return block_table
|
||||
end
|
||||
|
||||
function Grid:clearClearedRows()
|
||||
for row = 1, 24 do
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
for above_row = row, 2, -1 do
|
||||
self.grid[above_row] = self.grid[above_row - 1]
|
||||
self.grid_age[above_row] = self.grid_age[above_row - 1]
|
||||
end
|
||||
self.grid[1] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
self.grid[1] = {}
|
||||
self.grid_age[1] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[1][i] = empty
|
||||
self.grid_age[1][i] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:copyBottomRow()
|
||||
for row = 1, 23 do
|
||||
for row = 1, self.height - 1 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
|
||||
self.grid[self.height] = {}
|
||||
self.grid_age[self.height] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[self.height][i] = (self.grid[self.height - 1][i] == empty) and empty or block
|
||||
self.grid_age[self.height][i] = 0
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:garbageRise(row_vals)
|
||||
for row = 1, 23 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (row_vals[col] == "e") and empty or block
|
||||
for row = 1, self.height - 1 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyFourWide()
|
||||
for row = 1, 24 do
|
||||
local x = self.grid[row]
|
||||
x[1] = x[1]~=block and block or x[1]
|
||||
x[2] = x[2]~=block and block or x[2]
|
||||
x[3] = x[3]~=block and block or x[3]
|
||||
x[8] = x[8]~=block and block or x[8]
|
||||
x[9] = x[9]~=block and block or x[9]
|
||||
x[10] = x[10]~=block and block or x[10]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyCeiling(lines)
|
||||
for row = 1, lines do
|
||||
for col = 1, 9 do
|
||||
self.grid[row][col] = block
|
||||
end
|
||||
self.grid[self.height] = {}
|
||||
self.grid_age[self.height] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[self.height][i] = (row_vals[i] == "e") and empty or block
|
||||
self.grid_age[self.height][i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:clearSpecificRow(row)
|
||||
for col = 1, 10 do
|
||||
for col = 1, self.width do
|
||||
self.grid[row][col] = empty
|
||||
end
|
||||
end
|
||||
@@ -194,7 +191,7 @@ function Grid:applyPiece(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
x = piece.position.x + offset.x
|
||||
y = piece.position.y + offset.y
|
||||
if y + 1 > 0 and y < 24 then
|
||||
if y + 1 > 0 and y < self.height then
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
@@ -210,7 +207,7 @@ function Grid:applyBigPiece(piece)
|
||||
y = piece.position.y + offset.y
|
||||
for a = 1, 2 do
|
||||
for b = 1, 2 do
|
||||
if y*2+a > 0 then
|
||||
if y*2+a > 0 and y*2 < self.height then
|
||||
self.grid[y*2+a][x*2+b] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
@@ -222,8 +219,8 @@ function Grid:applyBigPiece(piece)
|
||||
end
|
||||
|
||||
function Grid:checkForBravo(cleared_row_count)
|
||||
for i = 0, 23 - cleared_row_count do
|
||||
for j = 0, 9 do
|
||||
for i = 0, self.height - 1 - cleared_row_count do
|
||||
for j = 0, self.width - 1 do
|
||||
if self:isOccupied(j, i) then return false end
|
||||
end
|
||||
end
|
||||
@@ -231,9 +228,9 @@ function Grid:checkForBravo(cleared_row_count)
|
||||
end
|
||||
|
||||
function Grid:checkStackHeight()
|
||||
for i = 0, 23 do
|
||||
for j = 0, 9 do
|
||||
if self:isOccupied(j, i) then return 24 - i end
|
||||
for i = 0, self.height - 1 do
|
||||
for j = 0, self.width - 1 do
|
||||
if self:isOccupied(j, i) then return self.height - i end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
@@ -274,8 +271,8 @@ function Grid:checkSecretGrade()
|
||||
end
|
||||
|
||||
function Grid:hasGemBlocks()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x].skin == "gem" then
|
||||
return true
|
||||
end
|
||||
@@ -286,16 +283,16 @@ end
|
||||
|
||||
function Grid:mirror()
|
||||
local new_grid = {}
|
||||
for y = 1, 24 do
|
||||
for y = 1, self.height do
|
||||
new_grid[y] = {}
|
||||
for x = 1, 10 do
|
||||
for x = 1, self.width do
|
||||
new_grid[y][x] = empty
|
||||
end
|
||||
end
|
||||
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
new_grid[y][x] = self.grid[y][11 - x]
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
new_grid[y][x] = self.grid[y][self.width + 1 - x]
|
||||
end
|
||||
end
|
||||
self.grid = new_grid
|
||||
@@ -310,9 +307,83 @@ function Grid:applyMap(map)
|
||||
end
|
||||
end
|
||||
|
||||
-- inefficient algorithm for squares
|
||||
function Grid:markSquares()
|
||||
-- goes up by 1 for silver, 2 for gold
|
||||
local square_count = 0
|
||||
for i = 1, 2 do
|
||||
for y = 5, self.height - 3 do
|
||||
for x = 1, self.width - 3 do
|
||||
local age_table = {}
|
||||
local age_count = 0
|
||||
local colour_table = {}
|
||||
local is_square = true
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
if self.grid[y+j][x+k].skin == "" or self.grid[y+j][x+k].skin == "square" then
|
||||
is_square = false
|
||||
end
|
||||
if age_table[self.grid_age[y+j][x+k]] == nil then
|
||||
age_table[self.grid_age[y+j][x+k]] = 1
|
||||
age_count = age_count + 1
|
||||
else
|
||||
age_table[self.grid_age[y+j][x+k]] = age_table[self.grid_age[y+j][x+k]] + 1
|
||||
end
|
||||
if age_count > 4 or age_table[self.grid_age[y+j][x+k]] > 4 then
|
||||
is_square = false
|
||||
end
|
||||
if not table.contains(colour_table, self.grid[y+j][x+k].colour) then
|
||||
table.insert(colour_table, self.grid[y+j][x+k].colour)
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_square then
|
||||
if i == 1 and #colour_table == 1 then
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
self.grid[y+j][x+k].colour = "Y"
|
||||
self.grid[y+j][x+k].skin = "square"
|
||||
end
|
||||
end
|
||||
square_count = square_count + 2
|
||||
elseif i == 2 then
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
self.grid[y+j][x+k].colour = "F"
|
||||
self.grid[y+j][x+k].skin = "square"
|
||||
end
|
||||
|
||||
end
|
||||
square_count = square_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return square_count
|
||||
end
|
||||
|
||||
-- square scan
|
||||
function Grid:scanForSquares()
|
||||
local table = {}
|
||||
for row = 1, self.height do
|
||||
local silver = 0
|
||||
local gold = 0
|
||||
for col = 1, self.width do
|
||||
local colour = self.grid[row][col].colour
|
||||
if self.grid[row][col].skin == "square" then
|
||||
if colour == "Y" then gold = gold + 1
|
||||
else silver = silver + 1 end
|
||||
end
|
||||
end
|
||||
table[row] = gold * 2.5 + silver * 1.25
|
||||
end
|
||||
return table
|
||||
end
|
||||
|
||||
function Grid:update()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
self.grid_age[y][x] = self.grid_age[y][x] + 1
|
||||
end
|
||||
@@ -321,33 +392,37 @@ function Grid:update()
|
||||
end
|
||||
|
||||
function Grid:draw()
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
if self.grid[y][x] ~= empty then
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
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
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
|
||||
else
|
||||
if self.grid[y][x].skin == "bone" then
|
||||
if self.grid[y][x].colour == "X" then
|
||||
love.graphics.setColor(0, 0, 0, 0)
|
||||
elseif self.grid[y][x].skin == "bone" then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
else
|
||||
else
|
||||
love.graphics.setColor(0.5, 0.5, 0.5, 1)
|
||||
end
|
||||
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
||||
end
|
||||
if self.grid[y][x].skin ~= "bone" 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.setLineWidth(1)
|
||||
if y > 1 and self.grid[y-1][x] == empty 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)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(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)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -357,21 +432,22 @@ function Grid:draw()
|
||||
end
|
||||
|
||||
function Grid:drawOutline()
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
if self.grid[y][x] ~= empty then
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
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.setLineWidth(1)
|
||||
if y > 1 and self.grid[y-1][x] == empty 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)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(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)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -382,11 +458,11 @@ end
|
||||
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
|
||||
lock_flash = lock_flash == nil and true or lock_flash
|
||||
brightness = brightness == nil and 0.5 or brightness
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid[y][x].colour == "X" then
|
||||
opacity = 1
|
||||
opacity = 0
|
||||
elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
|
||||
opacity = garbage_opacity_function(self.grid_age[y][x])
|
||||
else
|
||||
@@ -398,16 +474,17 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
|
||||
if opacity > 0 and self.grid[y][x].colour ~= "X" then
|
||||
love.graphics.setColor(0.64, 0.64, 0.64)
|
||||
love.graphics.setLineWidth(1)
|
||||
if y > 1 and self.grid[y-1][x] == empty 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)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(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)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -426,31 +503,32 @@ function Grid:drawCustom(colour_function, gamestate)
|
||||
|
||||
gamestate: the gamemode instance itself to pass in colour_function
|
||||
]]
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
local block = self.grid[y][x]
|
||||
if block ~= empty then
|
||||
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
|
||||
A = 1
|
||||
A = 0
|
||||
end
|
||||
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)
|
||||
if outline > 0 and self.grid[y][x].colour ~= "X" then
|
||||
love.graphics.setColor(0.64, 0.64, 0.64, outline)
|
||||
love.graphics.setLineWidth(1)
|
||||
if y > 1 and self.grid[y-1][x] == empty then
|
||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty then
|
||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
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)
|
||||
end
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(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)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -104,9 +104,8 @@ function Piece:dropToBottom(grid)
|
||||
self:dropSquares(math.huge, grid)
|
||||
self.gravity = 0
|
||||
if self.position.y > piece_y then
|
||||
-- if it got dropped any, also reset lock delay
|
||||
if self.ghost == false then playSE("bottom") end
|
||||
self.lock_delay = 0
|
||||
-- self.lock_delay = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -118,19 +117,36 @@ function Piece:lockIfBottomed(grid)
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:addGravity(gravity, grid)
|
||||
function Piece:addGravity(gravity, grid, classic_lock)
|
||||
local new_gravity = self.gravity + gravity
|
||||
if self:isDropBlocked(grid) then
|
||||
self.gravity = math.min(1, new_gravity)
|
||||
self.lock_delay = self.lock_delay + 1
|
||||
else
|
||||
local dropped_squares = math.floor(new_gravity)
|
||||
local new_frac_gravity = new_gravity - dropped_squares
|
||||
self.gravity = new_frac_gravity
|
||||
self:dropSquares(dropped_squares, grid)
|
||||
if self:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
if classic_lock then
|
||||
self.gravity = math.min(1, new_gravity)
|
||||
else
|
||||
self.gravity = 0
|
||||
self.lock_delay = self.lock_delay + 1
|
||||
end
|
||||
elseif not (
|
||||
self:isMoveBlocked(grid, { x=0, y=-1 }) and gravity < 0
|
||||
) then
|
||||
local dropped_squares = math.floor(math.abs(new_gravity))
|
||||
if gravity >= 0 then
|
||||
local new_frac_gravity = new_gravity - dropped_squares
|
||||
self.gravity = new_frac_gravity
|
||||
self:dropSquares(dropped_squares, grid)
|
||||
if self:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
else
|
||||
local new_frac_gravity = new_gravity + dropped_squares
|
||||
self.gravity = new_frac_gravity
|
||||
self:moveInGrid({ x=0, y=-1 }, dropped_squares, grid)
|
||||
if self:isMoveBlocked(grid, { x=0, y=-1 }) then
|
||||
playSE("bottom")
|
||||
end
|
||||
end
|
||||
else
|
||||
self.gravity = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1,138 +1,24 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local MarathonA2Game = require 'tetris.modes.marathon_a2'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
local BigA2Game = MarathonA2Game:extend()
|
||||
|
||||
local MarathonA2Game = GameMode:extend()
|
||||
BigA2Game.name = "Big A2"
|
||||
BigA2Game.hash = "BigA2"
|
||||
BigA2Game.tagline = "Big blocks in the most celebrated TGM mode!"
|
||||
|
||||
MarathonA2Game.name = "Big A2"
|
||||
MarathonA2Game.hash = "BigA2"
|
||||
MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible roll? Big mode too!"
|
||||
|
||||
|
||||
|
||||
|
||||
function MarathonA2Game:new()
|
||||
self.super:new()
|
||||
function BigA2Game:new()
|
||||
BigA2Game.super:new()
|
||||
self.big_mode = true
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
|
||||
self.grade = 0
|
||||
self.grade_points = 0
|
||||
self.grade_point_decay_counter = 0
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.lock_hard_drop = false
|
||||
self.enable_hold = false
|
||||
self.next_queue_length = 1
|
||||
end
|
||||
|
||||
function MarathonA2Game:getARE()
|
||||
if self.level < 700 then return 27
|
||||
elseif self.level < 800 then return 18
|
||||
else return 14 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getLineARE()
|
||||
if self.level < 600 then return 27
|
||||
elseif self.level < 700 then return 18
|
||||
elseif self.level < 800 then return 14
|
||||
else return 8 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 < 0 then return false end
|
||||
if self.roll_frames > 3694 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function 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)
|
||||
cleared_row_count = cleared_row_count / 2
|
||||
self.level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 and not self.clear then
|
||||
self.clear = true
|
||||
self.grid:clear()
|
||||
self.roll_frames = -150
|
||||
end
|
||||
self.lock_drop = self.level >= 900
|
||||
self.lock_hard_drop = self.level >= 900
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
function BigA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
cleared_lines = cleared_lines / 2
|
||||
if not self.clear then
|
||||
cleared_lines = cleared_lines / 2
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines >= 4 then
|
||||
self.tetris_count = self.tetris_count + 1
|
||||
end
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
@@ -144,164 +30,21 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
else self.lines = self.lines + cleared_lines end
|
||||
end
|
||||
|
||||
function BigA2Game:onLineClear(cleared_row_count)
|
||||
cleared_row_count = cleared_row_count / 2
|
||||
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
|
||||
self.grid:clear()
|
||||
if self:qualifiesForMRoll() then self.grade = 32 end
|
||||
self.roll_frames = -150
|
||||
end
|
||||
self.lock_drop = self.level >= 900
|
||||
self.lock_hard_drop = self.level >= 900
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
function MarathonA2Game:drawGrid(ruleset)
|
||||
if self.clear and not (self.completed or self.game_over) then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
|
||||
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 .. " " ..
|
||||
strTrueValues(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)
|
||||
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
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
|
||||
return BigA2Game
|
||||
@@ -5,14 +5,18 @@ local playedReadySE = false
|
||||
local playedGoSE = false
|
||||
|
||||
local Grid = require 'tetris.components.grid'
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
local Randomizer = require 'tetris.randomizers.bag7'
|
||||
local BagRandomizer = require 'tetris.randomizers.bag'
|
||||
|
||||
local GameMode = Object:extend()
|
||||
|
||||
GameMode.name = ""
|
||||
GameMode.hash = ""
|
||||
GameMode.tagline = ""
|
||||
GameMode.rollOpacityFunction = function(age) return 0 end
|
||||
|
||||
function GameMode:new()
|
||||
self.grid = Grid()
|
||||
function GameMode:new(secret_inputs)
|
||||
self.grid = Grid(10, 24)
|
||||
self.randomizer = Randomizer()
|
||||
self.piece = nil
|
||||
self.ready_frames = 100
|
||||
@@ -21,6 +25,7 @@ function GameMode:new()
|
||||
self.score = 0
|
||||
self.level = 0
|
||||
self.lines = 0
|
||||
self.squares = 0
|
||||
self.drop_bonus = 0
|
||||
self.are = 0
|
||||
self.lcd = 0
|
||||
@@ -40,11 +45,14 @@ function GameMode:new()
|
||||
self.enable_hard_drop = true
|
||||
self.next_queue_length = 1
|
||||
self.additive_gravity = true
|
||||
self.classic_lock = false
|
||||
self.draw_section_times = false
|
||||
self.draw_secondary_section_times = false
|
||||
self.big_mode = false
|
||||
self.irs = true
|
||||
self.ihs = true
|
||||
self.square_mode = false
|
||||
self.immobile_spin_bonus = false
|
||||
self.rpc_details = "In game"
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
@@ -56,6 +64,9 @@ function GameMode:new()
|
||||
self.hard_drop_locked = false
|
||||
self.lock_on_soft_drop = false
|
||||
self.lock_on_hard_drop = false
|
||||
self.cleared_block_table = {}
|
||||
self.last_lcd = 0
|
||||
self.used_randomizer = nil
|
||||
self.hold_queue = nil
|
||||
self.held = false
|
||||
self.section_start_time = 0
|
||||
@@ -70,20 +81,34 @@ 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:getDasCutDelay() return 0 end
|
||||
function GameMode:getGravity() return 1/64 end
|
||||
|
||||
function GameMode:getNextPiece(ruleset)
|
||||
|
||||
return {
|
||||
skin = "2tie",
|
||||
shape = self.randomizer:nextPiece(),
|
||||
skin = self:getSkin(),
|
||||
shape = self.used_randomizer:nextPiece(),
|
||||
orientation = ruleset:getDefaultOrientation(),
|
||||
}
|
||||
end
|
||||
|
||||
function GameMode:getSkin()
|
||||
return "2tie"
|
||||
end
|
||||
|
||||
function GameMode:initialize(ruleset)
|
||||
-- generate next queue
|
||||
self:new()
|
||||
for i = 1, self.next_queue_length do
|
||||
self.used_randomizer = (
|
||||
ruleset.pieces == self.randomizer.possible_pieces and
|
||||
self.randomizer or
|
||||
(
|
||||
ruleset.pieces == 7 and
|
||||
Randomizer() or
|
||||
BagRandomizer(ruleset.pieces)
|
||||
)
|
||||
)
|
||||
self.ruleset = ruleset
|
||||
for i = 1, math.max(self.next_queue_length, 1) do
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
|
||||
@@ -91,14 +116,10 @@ function GameMode:initialize(ruleset)
|
||||
end
|
||||
|
||||
function GameMode:update(inputs, ruleset)
|
||||
if self.game_over then
|
||||
if self.game_over or self.completed 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
|
||||
|
||||
if config.gamesettings.diagonal_input == 2 then
|
||||
if inputs["left"] or inputs["right"] then
|
||||
@@ -113,16 +134,16 @@ function GameMode:update(inputs, ruleset)
|
||||
-- advance one frame
|
||||
if self:advanceOneFrame(inputs, ruleset) == false then return end
|
||||
|
||||
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
|
||||
self:chargeDAS(inputs, self:getDasLimit(), self:getARR())
|
||||
|
||||
-- set attempt flags
|
||||
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end
|
||||
if
|
||||
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece, self.grid) end
|
||||
if (
|
||||
inputs["rotate_left"] or inputs["rotate_right"] or
|
||||
inputs["rotate_left2"] or inputs["rotate_right2"] or
|
||||
inputs["rotate_180"]
|
||||
then
|
||||
self:onAttemptPieceRotate(self.piece)
|
||||
) then
|
||||
self:onAttemptPieceRotate(self.piece, self.grid)
|
||||
end
|
||||
|
||||
if self.piece == nil then
|
||||
@@ -135,27 +156,64 @@ function GameMode:update(inputs, ruleset)
|
||||
if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then
|
||||
self:hold(inputs, ruleset)
|
||||
self.prev_inputs = inputs
|
||||
if not self.grid:canPlacePiece(self.piece) then
|
||||
self.game_over = true
|
||||
end
|
||||
return
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
-- diff vars to use in checks
|
||||
local piece_y = self.piece.position.y
|
||||
local piece_x = self.piece.position.x
|
||||
local piece_rot = self.piece.rotation
|
||||
|
||||
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, self.additive_gravity
|
||||
self.enable_hard_drop, self.additive_gravity, self.classic_lock
|
||||
)
|
||||
|
||||
local piece_dy = self.piece.position.y - piece_y
|
||||
local piece_dx = self.piece.position.x - piece_x
|
||||
local piece_drot = self.piece.rotation - piece_rot
|
||||
|
||||
-- das cut
|
||||
if (
|
||||
(piece_dy ~= 0 and (inputs.up or inputs.down)) or
|
||||
(piece_drot ~= 0 and (
|
||||
inputs.rotate_left or inputs.rotate_right or
|
||||
inputs.rotate_left2 or inputs.rotate_right2 or
|
||||
inputs.rotate_180
|
||||
))
|
||||
) then
|
||||
self:dasCut()
|
||||
end
|
||||
|
||||
if (piece_dx ~= 0) then
|
||||
self.piece.last_rotated = false
|
||||
self:onPieceMove(self.piece, self.grid, piece_dx)
|
||||
end
|
||||
if (piece_dy ~= 0) then
|
||||
self.piece.last_rotated = false
|
||||
self:onPieceDrop(self.piece, self.grid, piece_dy)
|
||||
end
|
||||
if (piece_drot ~= 0) then
|
||||
self.piece.last_rotated = true
|
||||
self:onPieceRotate(self.piece, self.grid, piece_drot)
|
||||
end
|
||||
|
||||
if inputs["up"] == true and
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
@@ -168,7 +226,12 @@ function GameMode:update(inputs, ruleset)
|
||||
end
|
||||
|
||||
if inputs["down"] == true then
|
||||
self:onSoftDrop(piece_dy)
|
||||
if not (
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
piece_drot ~= 0
|
||||
) then
|
||||
self:onSoftDrop(piece_dy)
|
||||
end
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
not self.drop_locked and
|
||||
self.lock_on_soft_drop
|
||||
@@ -178,14 +241,29 @@ function GameMode:update(inputs, ruleset)
|
||||
end
|
||||
|
||||
if self.piece.locked == true then
|
||||
-- spin detection, immobile only for now
|
||||
if self.immobile_spin_bonus and
|
||||
self.piece.last_rotated and (
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=0, y=-1 })
|
||||
) then
|
||||
self.piece.spin = true
|
||||
end
|
||||
|
||||
self.grid:applyPiece(self.piece)
|
||||
self.grid:markClearedRows()
|
||||
|
||||
-- mark squares (can be overridden)
|
||||
if self.square_mode then
|
||||
self.squares = self.squares + self.grid:markSquares()
|
||||
end
|
||||
|
||||
local cleared_row_count = self.grid:getClearedRowCount()
|
||||
|
||||
self:onPieceLock(self.piece, cleared_row_count)
|
||||
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
|
||||
|
||||
self.cleared_block_table = self.grid:markClearedRows()
|
||||
self.piece = nil
|
||||
if self.enable_hold then
|
||||
self.held = false
|
||||
@@ -194,16 +272,20 @@ function GameMode:update(inputs, ruleset)
|
||||
if cleared_row_count > 0 then
|
||||
playSE("erase")
|
||||
self.lcd = self:getLineClearDelay()
|
||||
self.are = self:getLineARE()
|
||||
self.last_lcd = self.lcd
|
||||
self.are = (
|
||||
ruleset.are and self:getLineARE() or 0
|
||||
)
|
||||
if self.lcd == 0 then
|
||||
self.grid:clearClearedRows()
|
||||
self:afterLineClear(cleared_row_count)
|
||||
if self.are == 0 then
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
end
|
||||
end
|
||||
self:onLineClear(cleared_row_count)
|
||||
else
|
||||
if self:getARE() == 0 then
|
||||
if self:getARE() == 0 or not ruleset.are then
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
else
|
||||
self.are = self:getARE()
|
||||
@@ -226,13 +308,17 @@ end
|
||||
|
||||
-- event functions
|
||||
function GameMode:whilePieceActive() end
|
||||
function GameMode:onAttemptPieceMove(piece) end
|
||||
function GameMode:onAttemptPieceRotate(piece) end
|
||||
function GameMode:onAttemptPieceMove(piece, grid) end
|
||||
function GameMode:onAttemptPieceRotate(piece, grid) end
|
||||
function GameMode:onPieceMove(piece, grid, dx) end
|
||||
function GameMode:onPieceRotate(piece, grid, drot) end
|
||||
function GameMode:onPieceDrop(piece, grid, dy) end
|
||||
function GameMode:onPieceLock(piece, cleared_row_count)
|
||||
playSE("lock")
|
||||
end
|
||||
|
||||
function GameMode:onLineClear(cleared_row_count) end
|
||||
function GameMode:afterLineClear(cleared_row_count) end
|
||||
|
||||
function GameMode:onPieceEnter() end
|
||||
function GameMode:onHold() end
|
||||
@@ -247,8 +333,19 @@ end
|
||||
|
||||
function GameMode:onGameOver()
|
||||
switchBGM(nil)
|
||||
love.graphics.setColor(0, 0, 0, 1 - 2 ^ (-self.game_over_frames / 30))
|
||||
love.graphics.rectangle(
|
||||
"fill", 64, 80,
|
||||
16 * self.grid.width, 16 * (self.grid.height - 4)
|
||||
)
|
||||
end
|
||||
|
||||
function GameMode:onGameComplete()
|
||||
self:onGameOver()
|
||||
end
|
||||
|
||||
function GameMode:onExit() end
|
||||
|
||||
-- DAS functions
|
||||
|
||||
function GameMode:startRightDAS()
|
||||
@@ -289,7 +386,7 @@ function GameMode:stopDAS()
|
||||
end
|
||||
|
||||
function GameMode:chargeDAS(inputs)
|
||||
if config["das_last_key"] then
|
||||
if config.gamesettings.das_last_key == 2 then
|
||||
if inputs["right"] == true and self.das.direction ~= "right" and not self.prev_inputs["right"] then
|
||||
self:startRightDAS()
|
||||
elseif inputs["left"] == true and self.das.direction ~= "left" and not self.prev_inputs["left"] then
|
||||
@@ -312,6 +409,13 @@ function GameMode:chargeDAS(inputs)
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:dasCut()
|
||||
self.das.frames = math.max(
|
||||
self.das.frames - self:getDasCutDelay(),
|
||||
-(self:getDasCutDelay() + 1)
|
||||
)
|
||||
end
|
||||
|
||||
function GameMode:areCancel(inputs, ruleset)
|
||||
if ruleset.are_cancel and self.piece_hard_dropped and
|
||||
not self.prev_inputs.up and
|
||||
@@ -327,6 +431,12 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
playedGoSE = false
|
||||
end
|
||||
if self.ready_frames > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
if not playedReadySE then
|
||||
playedReadySE = true
|
||||
playSEOnce("ready")
|
||||
@@ -340,16 +450,30 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
end
|
||||
elseif self.lcd > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
self.lcd = self.lcd - 1
|
||||
self:areCancel(inputs, ruleset)
|
||||
if self.lcd == 0 then
|
||||
local cleared_row_count = self.grid:getClearedRowCount()
|
||||
self.grid:clearClearedRows()
|
||||
self:afterLineClear(cleared_row_count)
|
||||
playSE("fall")
|
||||
if self.are == 0 then
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
end
|
||||
end
|
||||
elseif self.are > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
self.are = self.are - 1
|
||||
self:areCancel(inputs, ruleset)
|
||||
if self.are == 0 then
|
||||
@@ -359,14 +483,15 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
end
|
||||
|
||||
function GameMode:initializeOrHold(inputs, ruleset)
|
||||
if self.ihs and self.enable_hold and inputs["hold"] == true then
|
||||
if (
|
||||
(self.frames == 0 or (ruleset.are and self:getARE() ~= 0)) and self.ihs or false
|
||||
) and self.enable_hold and inputs["hold"] == true then
|
||||
self:hold(inputs, ruleset, true)
|
||||
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
|
||||
@@ -403,23 +528,63 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
|
||||
self.prev_inputs, self.move,
|
||||
self:getLockDelay(), self:getDropSpeed(),
|
||||
self.lock_drop, self.lock_hard_drop, self.big_mode,
|
||||
self.irs
|
||||
(
|
||||
self.frames == 0 or (ruleset.are and self:getARE() ~= 0)
|
||||
) and self.irs or false
|
||||
)
|
||||
if self.lock_drop then
|
||||
if self.buffer_hard_drop then
|
||||
if config.gamesettings.buffer_lock == 1 then
|
||||
self.piece:dropToBottom(self.grid)
|
||||
if self.lock_on_hard_drop then self.piece.locked = true end
|
||||
end
|
||||
local above_field = (
|
||||
(config.gamesettings.spawn_positions == 1 and
|
||||
ruleset.spawn_above_field) or
|
||||
config.gamesettings.spawn_positions == 3
|
||||
)
|
||||
self:onHardDrop(self.piece.position.y - (
|
||||
self.piece.big and
|
||||
ruleset.big_spawn_positions[self.piece.shape].y or
|
||||
ruleset.spawn_positions[self.piece.shape].y) +
|
||||
(above_field and ruleset:getAboveFieldOffset(
|
||||
piece_data.shape, piece_data.orientation
|
||||
) or 0)
|
||||
)
|
||||
self.buffer_hard_drop = false
|
||||
end
|
||||
if self.buffer_soft_drop then
|
||||
if (
|
||||
self.lock_on_soft_drop and
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
config.gamesettings.buffer_lock == 1
|
||||
) then
|
||||
self.piece.locked = true
|
||||
end
|
||||
self.buffer_soft_drop = false
|
||||
end
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
self.grid:canPlacePiece(self.piece) then
|
||||
playSE("bottom")
|
||||
end
|
||||
if self.lock_drop or (
|
||||
not ruleset.are or self:getARE() == 0
|
||||
) then
|
||||
self.drop_locked = true
|
||||
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
|
||||
end
|
||||
if generate_next_piece == nil then
|
||||
table.remove(self.next_queue, 1)
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
self:playNextSound()
|
||||
self:playNextSound(ruleset)
|
||||
end
|
||||
|
||||
function GameMode:playNextSound()
|
||||
playSE("blocks", self.next_queue[1].shape)
|
||||
function GameMode:playNextSound(ruleset)
|
||||
playSE("blocks", ruleset.next_sounds[self.next_queue[1].shape])
|
||||
end
|
||||
|
||||
function GameMode:getHighScoreData()
|
||||
@@ -428,14 +593,87 @@ function GameMode:getHighScoreData()
|
||||
}
|
||||
end
|
||||
|
||||
function GameMode:animation(x, y, skin, colour)
|
||||
return {
|
||||
1, 1, 1,
|
||||
-0.25 + 1.25 * (self.lcd / self.last_lcd),
|
||||
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.last_lcd),
|
||||
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.last_lcd - self.lcd) / self.last_lcd
|
||||
)
|
||||
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()
|
||||
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
|
||||
local b = (
|
||||
self.classic_lock and
|
||||
(
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
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
|
||||
|
||||
@@ -448,11 +686,16 @@ function GameMode:drawGhostPiece(ruleset)
|
||||
end
|
||||
|
||||
function GameMode:drawNextQueue(ruleset)
|
||||
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
local colourscheme
|
||||
if ruleset.pieces == 7 then
|
||||
colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
else
|
||||
colourscheme = ruleset.colourscheme
|
||||
end
|
||||
function drawPiece(piece, skin, offsets, pos_x, pos_y)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = offset.x + ruleset.spawn_positions[piece].x
|
||||
local y = offset.y + 4.7
|
||||
local x = offset.x + ruleset:getDrawOffset(piece, rotation).x + ruleset.spawn_positions[piece].x
|
||||
local y = offset.y + ruleset:getDrawOffset(piece, rotation).y + 4.7
|
||||
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
|
||||
end
|
||||
end
|
||||
@@ -468,8 +711,7 @@ function GameMode:drawNextQueue(ruleset)
|
||||
end
|
||||
end
|
||||
if self.hold_queue ~= nil and self.enable_hold then
|
||||
local hold_color = self.held and 0.6 or 1
|
||||
self:setHoldOpacity(1, hold_color)
|
||||
self:setHoldOpacity()
|
||||
drawPiece(
|
||||
self.hold_queue.shape,
|
||||
self.hold_queue.skin,
|
||||
@@ -480,15 +722,25 @@ function GameMode:drawNextQueue(ruleset)
|
||||
return false
|
||||
end
|
||||
|
||||
function GameMode:setNextOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
function GameMode:setNextOpacity(i)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
function GameMode:setHoldOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
|
||||
function GameMode:setHoldOpacity()
|
||||
local colour = self.held and 0.6 or 1
|
||||
love.graphics.setColor(colour, colour, colour, 1)
|
||||
end
|
||||
|
||||
function GameMode:getBackground()
|
||||
return 0
|
||||
end
|
||||
|
||||
function GameMode:getHighscoreData()
|
||||
return {}
|
||||
end
|
||||
|
||||
function GameMode:drawGrid()
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
function GameMode:drawScoringInfo()
|
||||
@@ -528,7 +780,8 @@ function GameMode:sectionColourFunction(section)
|
||||
return { 1, 1, 1, 1 }
|
||||
end
|
||||
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
|
||||
section_limit = section_limit or math.huge
|
||||
local section_x = 530
|
||||
local section_secondary_x = 440
|
||||
|
||||
@@ -553,10 +806,14 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
current_x = section_secondary_x
|
||||
end
|
||||
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||
if current_section <= section_limit then
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||
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 split_x = 530
|
||||
|
||||
@@ -564,14 +821,18 @@ function GameMode:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
for section, time in pairs(self.section_times) do
|
||||
if section > 0 then
|
||||
love.graphics.setColor(self:sectionColourFunction(section))
|
||||
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
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")
|
||||
if (current_section <= section_limit) then
|
||||
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
|
||||
end
|
||||
|
||||
function GameMode:drawCustom() end
|
||||
|
||||
@@ -256,8 +256,10 @@ end
|
||||
local function getSectionForLevel(level)
|
||||
if level < 2001 then
|
||||
return math.floor(level / 100) + 1
|
||||
else
|
||||
elseif level < 2020 then
|
||||
return 20
|
||||
else
|
||||
return 21
|
||||
end
|
||||
end
|
||||
|
||||
@@ -442,7 +444,7 @@ function Marathon2020Game:drawScoringInfo()
|
||||
love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
|
||||
self:drawSectionTimesWithSecondary(current_section)
|
||||
self:drawSectionTimesWithSecondary(current_section, 20)
|
||||
|
||||
if (self.cool_timer > 0) then
|
||||
love.graphics.printf("COOL!!", 64, 400, 160, "center")
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Grid = require 'tetris.components.grid'
|
||||
|
||||
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'
|
||||
|
||||
@@ -32,6 +33,7 @@ function MarathonA1Game:new()
|
||||
|
||||
self.randomizer = History4RollsRandomizer()
|
||||
|
||||
self.additive_gravity = false
|
||||
self.lock_drop = false
|
||||
self.enable_hard_drop = false
|
||||
self.enable_hold = false
|
||||
|
||||
@@ -26,6 +26,7 @@ function MarathonA2Game:new()
|
||||
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.tetris_count = 0
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
@@ -33,6 +34,7 @@ function MarathonA2Game:new()
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.additive_gravity = false
|
||||
self.lock_drop = false
|
||||
self.lock_hard_drop = false
|
||||
self.enable_hold = false
|
||||
@@ -130,6 +132,9 @@ end
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines >= 4 then
|
||||
self.tetris_count = self.tetris_count + 1
|
||||
end
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
@@ -145,6 +150,7 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
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
|
||||
@@ -164,6 +170,8 @@ function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
||||
section_time = self.frames - self.section_start_time
|
||||
self.section_times[math.floor(old_level / 100)] = section_time
|
||||
self.section_start_time = self.frames
|
||||
self.section_tetrises[math.floor(old_level / 100)] = self.tetris_count
|
||||
self.tetris_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -231,14 +239,16 @@ local grade_conversion = {
|
||||
17, 18, 19
|
||||
}
|
||||
|
||||
function MarathonA2Game:whilePieceActive()
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
if self.clear or cleared_lines == 0 then return
|
||||
else
|
||||
self.grade_points = self.grade_points + (
|
||||
math.ceil(
|
||||
|
||||
@@ -39,6 +39,7 @@ self.SGnames = {
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.additive_gravity = false
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.enable_hold = true
|
||||
@@ -313,13 +314,16 @@ local grade_conversion = {
|
||||
17
|
||||
}
|
||||
|
||||
function MarathonA3Game:whilePieceActive()
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
if cleared_lines == 0 then return
|
||||
else
|
||||
if self.clear then
|
||||
if self:qualifiesForMRoll() then
|
||||
@@ -424,8 +428,8 @@ function MarathonA3Game:drawScoringInfo()
|
||||
end
|
||||
|
||||
-- draw section time data
|
||||
current_section = math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSecondary(current_section)
|
||||
current_section = self.level >= 999 and 11 or math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSecondary(current_section, 10)
|
||||
--[[
|
||||
|
||||
section_x = 530
|
||||
|
||||
@@ -86,12 +86,8 @@ function PhantomMania2Game:getGarbageLimit()
|
||||
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(),
|
||||
}
|
||||
function PhantomMania2Game:getSkin()
|
||||
return self.level >= 1000 and "bone" or "2tie"
|
||||
end
|
||||
|
||||
function PhantomMania2Game:hitTorikan(old_level, new_level)
|
||||
|
||||
155
tetris/modes/race_40.lua
Normal file
@@ -0,0 +1,155 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
|
||||
|
||||
local Race40Game = GameMode:extend()
|
||||
|
||||
Race40Game.name = "Race 40"
|
||||
Race40Game.hash = "Race40"
|
||||
Race40Game.tagline = "How fast can you clear 40 lines?"
|
||||
|
||||
|
||||
function Race40Game:new()
|
||||
Race40Game.super:new()
|
||||
|
||||
self.lines = 0
|
||||
self.line_goal = 40
|
||||
self.pieces = 0
|
||||
self.randomizer = Bag7Randomiser()
|
||||
|
||||
self.roll_frames = 0
|
||||
|
||||
self.SGnames = {
|
||||
[0] = "",
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
self.upstacked = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.instant_hard_drop = true
|
||||
self.instant_soft_drop = false
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 6
|
||||
end
|
||||
|
||||
function Race40Game:getDropSpeed()
|
||||
return 20
|
||||
end
|
||||
|
||||
function Race40Game:getARR()
|
||||
return config.arr
|
||||
end
|
||||
|
||||
function Race40Game:getARE()
|
||||
return 0
|
||||
end
|
||||
|
||||
function Race40Game:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function Race40Game:getDasLimit()
|
||||
return config.das
|
||||
end
|
||||
|
||||
function Race40Game:getLineClearDelay()
|
||||
return 0
|
||||
end
|
||||
|
||||
function Race40Game:getLockDelay()
|
||||
return 30
|
||||
end
|
||||
|
||||
function Race40Game:getGravity()
|
||||
return 1/64
|
||||
end
|
||||
|
||||
function Race40Game:getDasCutDelay()
|
||||
return config.dcd
|
||||
end
|
||||
|
||||
function Race40Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames > 150 then
|
||||
self.completed = true
|
||||
end
|
||||
return false
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Race40Game:onPieceLock()
|
||||
self.super:onPieceLock()
|
||||
self.pieces = self.pieces + 1
|
||||
end
|
||||
|
||||
function Race40Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
self.lines = self.lines + cleared_row_count
|
||||
if self.lines >= self.line_goal then
|
||||
self.clear = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Race40Game:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
function Race40Game:getHighscoreData()
|
||||
return {
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function Race40Game:getSecretGrade(sg)
|
||||
if sg == 19 then self.upstacked = true end
|
||||
if self.upstacked then return self.SGnames[14 + math.floor((20 - sg) / 4)]
|
||||
else return self.SGnames[math.floor((sg / 19) * 14)] end
|
||||
end
|
||||
|
||||
function Race40Game:drawScoringInfo()
|
||||
Race40Game.super.drawScoringInfo(self)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
local text_x = config["side_next"] and 320 or 240
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("LINES", text_x, 320, 40, "left")
|
||||
love.graphics.printf("line/min", text_x, 160, 80, "left")
|
||||
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 7 or self.upstacked then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
|
||||
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
|
||||
if sg >= 7 or self.upstacked then
|
||||
love.graphics.printf(self:getSecretGrade(sg), 240, 450, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
|
||||
end
|
||||
|
||||
function Race40Game:getBackground()
|
||||
return 2
|
||||
end
|
||||
|
||||
return Race40Game
|
||||
@@ -1,568 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local SakuraRandomizer = require 'tetris.randomizers.sakura'
|
||||
|
||||
local SakuraGame = GameMode:extend()
|
||||
|
||||
SakuraGame.name = "Sakura A3"
|
||||
SakuraGame.hash = "SakuraA3"
|
||||
SakuraGame.tagline = "Clear away the Gem Blocks as fast as possible!"
|
||||
|
||||
local b = {
|
||||
["r"] = { skin = "2tie", colour = "R" },
|
||||
["o"] = { skin = "2tie", colour = "O" },
|
||||
["y"] = { skin = "2tie", colour = "Y" },
|
||||
["g"] = { skin = "2tie", colour = "G" },
|
||||
["c"] = { skin = "2tie", colour = "C" },
|
||||
["b"] = { skin = "2tie", colour = "B" },
|
||||
["m"] = { skin = "2tie", colour = "M" },
|
||||
["R"] = { skin = "gem", colour = "R" },
|
||||
["O"] = { skin = "gem", colour = "O" },
|
||||
["Y"] = { skin = "gem", colour = "Y" },
|
||||
["G"] = { skin = "gem", colour = "G" },
|
||||
["C"] = { skin = "gem", colour = "C" },
|
||||
["B"] = { skin = "gem", colour = "B" },
|
||||
["M"] = { skin = "gem", colour = "M" },
|
||||
}
|
||||
|
||||
local effects = {
|
||||
[4] = "mirror",
|
||||
[8] = "xray",
|
||||
[12] = "color",
|
||||
[13] = "mirror",
|
||||
[16] = "roll",
|
||||
[23] = "big"
|
||||
}
|
||||
|
||||
local maps = {
|
||||
[1] = {
|
||||
[22] = {nil, nil, b.O, b.R, nil, nil, b.M, b.m, nil, nil},
|
||||
[23] = {nil, b.G, b.c, b.c, b.c, b.c, b.c, b.c, b.Y, nil},
|
||||
[24] = {nil, b.C, b.y, b.y, b.y, b.y, b.y, b.y, b.B, nil},
|
||||
},
|
||||
[2] = {
|
||||
[20] = {nil, nil, nil, nil, b.G, b.b, b.b, b.M, nil, nil},
|
||||
[21] = {nil, nil, nil, nil, b.c, b.c, b.c, b.c, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, nil, b.R, b.Y, b.O, nil, nil},
|
||||
[23] = {nil, b.B, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil},
|
||||
[24] = {nil, b.b, b.b, b.b, b.b, b.b, b.b, b.b, b.C, nil},
|
||||
},
|
||||
[3] = {
|
||||
[20] = {nil, nil, nil, b.R, b.m, b.o, b.M, nil, nil, nil},
|
||||
[21] = {nil, nil, nil, nil, b.o, b.O, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
|
||||
[23] = {nil, b.m, b.o, b.m, b.o, b.m, b.o, b.m, b.o, nil},
|
||||
[24] = {nil, b.B, b.m, b.o, b.m, b.o, b.m, b.o, b.C, nil},
|
||||
},
|
||||
[4] = {
|
||||
[21] = {nil, nil, b.O, b.g, b.g, b.g, b.g, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, b.R, b.M, b.b, b.b, nil, nil, nil},
|
||||
[23] = {b.G, nil, b.Y, b.g, b.g, b.g, b.g, b.g, nil, nil},
|
||||
[24] = {b.b, b.C, b.b, b.b, b.b, b.b, b.b, b.b, b.B, nil},
|
||||
},
|
||||
[5] = {
|
||||
[16] = {nil, b.B, b.c, b.y, b.c, b.G, b.c, b.y, b.C, nil},
|
||||
[22] = {nil, nil, b.c, b.y, b.c, b.y, b.c, b.y, nil, nil},
|
||||
[23] = {nil, b.O, b.y, b.c, b.y, b.c, b.y, b.c, b.Y, nil},
|
||||
[24] = {nil, b.R, b.c, b.y, b.c, b.y, b.c, b.y, b.M, nil},
|
||||
},
|
||||
[6] = {
|
||||
[21] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, b.R, nil, b.b, b.y, nil, b.M, nil, nil},
|
||||
[23] = {nil, nil, nil, nil, b.y, b.b, nil, nil, nil, nil},
|
||||
[24] = {nil, b.G, b.y, b.b, b.C, b.y, b.b, b.y, b.B, nil},
|
||||
},
|
||||
[7] = {
|
||||
[20] = {nil, b.C, b.G, nil, b.r, b.g, b.r, b.g, nil, nil},
|
||||
[21] = {nil, nil, nil, nil, b.R, b.M, b.g, b.r, nil, nil},
|
||||
[22] = {b.r, nil, nil, nil, b.r, b.g, b.O, b.Y, nil, nil},
|
||||
[23] = {b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.r, nil, nil},
|
||||
[24] = {b.r, b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.B, nil},
|
||||
},
|
||||
[8] = {
|
||||
[15] = {nil, nil, nil, b.B, b.m, b.m, b.m, b.m, b.m, b.C},
|
||||
[16] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
|
||||
[17] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
|
||||
[18] = {nil, b.Y, b.y, b.y, b.y, b.y, b.y, b.y, b.y, b.G},
|
||||
[21] = {b.b, b.b, b.b, b.b, b.b, b.b, b.O, nil, nil, nil},
|
||||
[22] = {b.b, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||
[23] = {b.M, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||
[24] = {b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.R, nil},
|
||||
},
|
||||
[9] = {
|
||||
[18] = {nil, nil, nil, b.Y, b.m, b.m, b.m, b.m, nil, nil},
|
||||
[19] = {nil, nil, nil, b.c, b.c, b.c, b.c, b.G, nil, nil},
|
||||
[20] = {b.m, b.m, b.m, b.O, b.M, b.R, nil, nil, nil, nil},
|
||||
[21] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil, nil},
|
||||
[22] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, nil, nil},
|
||||
[23] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.C, nil},
|
||||
[24] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.B, nil},
|
||||
},
|
||||
[10] = {
|
||||
[18] = {nil, nil, nil, b.C, b.g, b.g, b.B, nil, nil, nil},
|
||||
[19] = {nil, nil, b.G, b.g, b.g, b.g, b.g, b.Y, nil, nil},
|
||||
[20] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.g, b.O, nil},
|
||||
[21] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, nil, b.c, nil, b.o, nil, nil, nil},
|
||||
[24] = {nil, nil, nil, nil, b.R, b.o, b.o, nil, nil, nil},
|
||||
},
|
||||
[11] = {
|
||||
[18] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[19] = {nil, nil, nil, nil, b.R, nil, nil, nil, nil, nil},
|
||||
[20] = {nil, nil, nil, nil, b.r, b.O, nil, nil, nil, nil},
|
||||
[21] = {nil, nil, nil, nil, nil, b.M, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
|
||||
[24] = {b.C, b.g, b.g, nil, b.o, b.o, nil, b.g, b.g, b.B},
|
||||
},
|
||||
[12] = {
|
||||
[21] = {nil, nil, nil, nil, nil, nil, nil, b.g, b.g, b.Y},
|
||||
[22] = {nil, nil, b.r, b.G, b.r, nil, nil, nil, b.O, b.g},
|
||||
[23] = {nil, b.r, b.C, b.r, b.B, b.r, nil, nil, nil, b.M},
|
||||
[24] = {b.r, b.r, b.r, b.R, b.r, b.r, b.r, nil, nil, nil},
|
||||
},
|
||||
[13] = {
|
||||
[20] = {b.c, nil, nil, nil, nil, nil, nil, nil, nil, b.B},
|
||||
[21] = {b.c, b.c, nil, nil, nil, nil, nil, nil, b.C, b.c},
|
||||
[22] = {b.c, b.c, b.c, nil, nil, nil, nil, b.G, b.c, b.c},
|
||||
[23] = {b.b, b.b, b.b, b.b, nil, nil, b.Y, b.b, b.b, b.b},
|
||||
[24] = {nil, b.M, b.b, b.b, b.b, b.O, b.b, b.b, b.R, nil},
|
||||
},
|
||||
[14] = {
|
||||
[20] = {nil, nil, nil, b.y, b.r, b.y, nil, nil, nil, nil},
|
||||
[21] = {b.R, nil, nil, b.Y, b.y, b.r, b.G, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, b.y, b.r, b.y, b.r, b.y, b.r, b.B},
|
||||
[23] = {nil, nil, nil, nil, nil, nil, nil, b.O, b.y, b.r},
|
||||
[24] = {nil, nil, nil, nil, nil, nil, b.M, b.y, b.r, b.C},
|
||||
},
|
||||
[15] = {
|
||||
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
|
||||
[18] = {nil, nil, b.b, b.y, b.b, b.b, b.y, b.b, nil, nil},
|
||||
[19] = {nil, nil, nil, b.y, b.b, b.b, b.y, nil, nil, nil},
|
||||
[20] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, b.M, b.R, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, b.G, b.y, b.y, b.C, nil, nil, nil},
|
||||
[24] = {nil, nil, b.B, b.y, b.y, b.y, b.y, b.y, nil, nil},
|
||||
},
|
||||
[16] = {
|
||||
[18] = {nil, nil, b.O, nil, nil, nil, nil, b.B, nil, nil},
|
||||
[19] = {nil, nil, b.c, nil, nil, b.G, nil, b.c, nil, nil},
|
||||
[20] = {nil, nil, b.c, nil, b.C, b.R, nil, b.c, nil, nil},
|
||||
[21] = {nil, nil, b.c, nil, nil, nil, nil, b.c, nil, nil},
|
||||
[22] = {nil, nil, b.Y, b.c, b.c, b.c, b.c, b.M, nil, nil},
|
||||
},
|
||||
[17] = {
|
||||
[15] = {b.O, nil, nil, b.g, nil, nil, b.m, nil, nil, b.Y},
|
||||
[16] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
|
||||
[17] = {nil, nil, nil, b.g, nil, nil, b.R, nil, nil, nil},
|
||||
[18] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
|
||||
[19] = {nil, nil, nil, b.M, nil, nil, b.m, nil, nil, nil},
|
||||
[20] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
|
||||
[21] = {nil, nil, nil, b.g, nil, nil, b.G, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
|
||||
[24] = {nil, b.m, b.m, b.m, b.B, b.C, b.g, b.g, b.g, nil},
|
||||
},
|
||||
[18] = {
|
||||
[19] = {nil, nil, nil, b.y, b.B, b.y, b.y, nil, nil, nil},
|
||||
[20] = {nil, nil, b.y, nil, nil, nil, nil, b.y, nil, nil},
|
||||
[21] = {nil, b.o, nil, nil, b.C, b.R, nil, nil, b.O, nil},
|
||||
[22] = {nil, b.M, nil, nil, b.Y, b.G, nil, nil, b.o, nil},
|
||||
[23] = {nil, b.r, b.o, nil, nil, nil, nil, b.o, b.r, nil},
|
||||
[24] = {nil, b.r, b.r, b.o, b.o, b.o, b.o, b.r, b.r, nil},
|
||||
},
|
||||
[19] = {
|
||||
[15] = {nil, nil, nil, nil, nil, nil, b.O, nil, nil, nil},
|
||||
[16] = {nil, nil, nil, nil, nil, b.o, nil, nil, nil, nil},
|
||||
[17] = {nil, nil, nil, nil, b.o, b.r, b.o, nil, nil, nil},
|
||||
[18] = {nil, b.o, nil, nil, b.o, b.r, b.r, b.o, nil, nil},
|
||||
[19] = {nil, b.o, b.o, nil, nil, b.R, b.r, b.r, nil, nil},
|
||||
[20] = {nil, nil, b.M, b.r, nil, b.r, b.r, b.r, b.r, nil},
|
||||
[21] = {nil, nil, b.r, b.r, b.r, b.r, b.y, b.G, b.o, b.o},
|
||||
[22] = {nil, b.r, b.o, b.y, b.Y, b.y, b.y, b.y, b.y, b.o},
|
||||
[23] = {nil, b.o, b.o, b.y, b.y, b.c, b.c, b.c, b.c, b.C},
|
||||
[24] = {nil, nil, b.o, b.y, b.b, b.b, b.B, b.b, b.b, b.b},
|
||||
},
|
||||
[20] = {
|
||||
[20] = {nil, nil, b.B, b.b, b.b, b.b, b.b, b.b, nil, nil},
|
||||
[21] = {b.c, nil, nil, b.C, b.c, b.c, b.c, nil, nil, b.c},
|
||||
[22] = {b.g, b.g, nil, nil, b.G, b.g, nil, nil, b.g, b.g},
|
||||
[23] = {b.y, b.y, b.o, nil, nil, nil, nil, b.Y, b.y, b.y},
|
||||
[24] = {b.r, b.r, b.r, b.R, nil, nil, b.M, b.r, b.r, b.r},
|
||||
},
|
||||
[21] = {
|
||||
[16] = {nil, nil, b.g, b.g, b.g, b.g, b.g, nil, nil, nil},
|
||||
[17] = {nil, b.g, b.g, b.g, b.g, b.g, b.g, b.B, b.g, nil},
|
||||
[18] = {b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.C, nil},
|
||||
[19] = {b.g, nil, nil, b.g, b.o, b.o, b.g, nil, nil, b.g},
|
||||
[20] = {nil, b.R, nil, b.g, b.o, b.o, nil, b.M, nil, b.G},
|
||||
[21] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[24] = {nil, nil, nil, nil, b.Y, b.O, nil, nil, nil, nil},
|
||||
},
|
||||
[22] = {
|
||||
[15] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
|
||||
[16] = {nil, b.b, b.O, b.b, nil, nil, b.b, b.Y, b.b, nil},
|
||||
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
|
||||
[20] = {nil, nil, nil, nil, b.R, b.M, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, b.b, b.C, b.G, b.b, nil, nil, nil},
|
||||
[24] = {nil, nil, nil, b.b, b.b, b.B, b.b, nil, nil, nil},
|
||||
},
|
||||
[23] = {
|
||||
[13] = {nil, nil, nil, nil, nil, nil, nil, nil, b.c, b.m},
|
||||
[14] = {nil, nil, nil, nil, nil, nil, nil, nil, b.y, b.g},
|
||||
[15] = {b.G, b.B, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||
[16] = {b.r, b.O, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||
[23] = {nil, nil, nil, nil, b.C, b.M, nil, nil, nil, nil},
|
||||
[24] = {nil, nil, nil, nil, b.R, b.Y, nil, nil, nil, nil},
|
||||
},
|
||||
[24] = {
|
||||
[20] = {b.g, b.g, b.g, b.g, b.G, nil, nil, nil, nil, nil},
|
||||
[21] = {nil, nil, nil, nil, nil, b.O, b.y, b.y, b.y, b.Y},
|
||||
[23] = {b.M, b.r, b.r, b.r, b.R, nil, nil, nil, nil, nil},
|
||||
[24] = {nil, nil, nil, nil, nil, b.M, b.r, b.r, b.r, b.R},
|
||||
},
|
||||
[25] = {
|
||||
[18] = {nil, nil, nil, nil, nil, b.B, nil, nil, nil, nil},
|
||||
[19] = {nil, nil, nil, b.G, nil, nil, nil, b.C, nil, nil},
|
||||
[20] = {nil, nil, nil, nil, nil, b.Y, nil, nil, nil, nil},
|
||||
[21] = {nil, nil, nil, b.M, nil, nil, nil, b.O, nil, nil},
|
||||
[22] = {nil, nil, nil, nil, nil, b.R, nil, nil, nil, nil},
|
||||
},
|
||||
[26] = {
|
||||
[13] = {nil, nil, nil, nil, b.r, b.r, nil, nil, nil, nil},
|
||||
[14] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[15] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[16] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
|
||||
[17] = {nil, nil, nil, b.O, b.o, b.o, b.Y, nil, nil, nil},
|
||||
[18] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
|
||||
[19] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
|
||||
[20] = {nil, nil, b.o, nil, nil, b.G, nil, b.o, nil, nil},
|
||||
[21] = {nil, nil, b.o, nil, b.M, b.R, nil, b.o, nil, nil},
|
||||
[22] = {nil, nil, b.o, b.b, b.b, b.b, b.B, b.o, nil, nil},
|
||||
[23] = {nil, nil, b.o, b.C, b.b, b.b, b.b, b.o, nil, nil},
|
||||
[24] = {nil, nil, b.o, b.o, b.o, b.o, b.o, b.o, nil, nil},
|
||||
},
|
||||
[27] = {
|
||||
[15] = {nil, b.C, b.o, b.g, b.g, b.g, b.g, b.g, b.B, nil},
|
||||
[16] = {b.g, nil, b.y, b.o, b.g, b.g, b.g, b.g, nil, b.y},
|
||||
[17] = {b.g, b.g, nil, b.y, b.o, b.g, b.g, nil, b.y, b.o},
|
||||
[18] = {b.g, b.g, b.g, nil, b.y, b.o, nil, b.y, b.o, b.g},
|
||||
[19] = {b.g, b.g, b.g, b.o, nil, b.G, b.y, b.o, b.g, b.g},
|
||||
[20] = {b.g, b.g, b.o, b.o, b.Y, nil, b.o, b.g, b.g, b.g},
|
||||
[21] = {b.g, b.o, b.y, nil, b.o, b.O, nil, b.g, b.g, b.g},
|
||||
[22] = {b.o, b.y, nil, b.g, b.g, b.o, b.y, nil, b.g, b.g},
|
||||
[23] = {b.y, nil, b.g, b.g, b.g, b.g, b.o, b.y, nil, b.g},
|
||||
[24] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.o, b.R, nil},
|
||||
},
|
||||
}
|
||||
|
||||
local STAGE_TRANSITION_TIME = 300
|
||||
|
||||
function SakuraGame:new()
|
||||
self.super:new()
|
||||
|
||||
self.randomizer = SakuraRandomizer()
|
||||
|
||||
self.current_map = 1
|
||||
self.time_limit = 10800
|
||||
self.cleared_frames = STAGE_TRANSITION_TIME
|
||||
self.stage_frames = 0
|
||||
self.time_extend = 0
|
||||
self.maps_cleared = 0
|
||||
self.map_20_time = 0
|
||||
self.stage_pieces = 0
|
||||
self.grid:applyMap(maps[self.current_map])
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function SakuraGame:checkRequirements()
|
||||
if self.maps_cleared >= 14 + 2 * (self.current_map - 20) and
|
||||
self.map_20_time <= frameTime(8,00) - frameTime(0,30) * (self.current_map - 20)
|
||||
then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SakuraGame:getGravity()
|
||||
if self.level < 8 then return 4/256
|
||||
elseif self.level < 19 then return 5/256
|
||||
elseif self.level < 35 then return 6/256
|
||||
elseif self.level < 40 then return 8/256
|
||||
elseif self.level < 50 then return 10/256
|
||||
elseif self.level < 60 then return 12/256
|
||||
elseif self.level < 70 then return 16/256
|
||||
elseif self.level < 80 then return 32/256
|
||||
elseif self.level < 90 then return 48/256
|
||||
elseif self.level < 100 then return 64/256
|
||||
elseif self.level < 108 then return 4/256
|
||||
elseif self.level < 119 then return 5/256
|
||||
elseif self.level < 125 then return 6/256
|
||||
elseif self.level < 131 then return 8/256
|
||||
elseif self.level < 139 then return 12/256
|
||||
elseif self.level < 149 then return 32/256
|
||||
elseif self.level < 156 then return 48/256
|
||||
elseif self.level < 164 then return 80/256
|
||||
elseif self.level < 174 then return 112/256
|
||||
elseif self.level < 180 then return 128/256
|
||||
elseif self.level < 200 then return 144/256
|
||||
elseif self.level < 212 then return 16/256
|
||||
elseif self.level < 221 then return 48/256
|
||||
elseif self.level < 232 then return 80/256
|
||||
elseif self.level < 244 then return 112/256
|
||||
elseif self.level < 256 then return 144/256
|
||||
elseif self.level < 267 then return 176/256
|
||||
elseif self.level < 277 then return 192/256
|
||||
elseif self.level < 287 then return 208/256
|
||||
elseif self.level < 295 then return 224/256
|
||||
elseif self.level < 300 then return 240/256
|
||||
else return 20 end
|
||||
end
|
||||
|
||||
function SakuraGame:onLineClear(cleared_row_count)
|
||||
self.level = self.level + cleared_row_count
|
||||
for i = 13, 24 do
|
||||
for j = 1, 10 do
|
||||
local block = self.grid.grid[i][j]
|
||||
if block and block.skin == "gem" and block.colour == "X" then
|
||||
self.time_limit = self.time_limit + 60
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SakuraGame:onPieceEnter()
|
||||
if self.level % 100 ~= 99 and not self.clear and self.stage_frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
if effects[self.current_map] == "mirror" and
|
||||
self.stage_pieces % 3 == 0 and self.stage_pieces ~= 0
|
||||
then
|
||||
self.grid:mirror()
|
||||
end
|
||||
self.stage_pieces = self.stage_pieces + 1
|
||||
end
|
||||
|
||||
function SakuraGame:advanceOneFrame(inputs, ruleset)
|
||||
if self.ready_frames == 0 then
|
||||
if self.lcd > 0 then
|
||||
if self.stage_frames <= frameTime(0,10) then self.time_extend = 600
|
||||
elseif self.stage_frames <= frameTime(0,30) then self.time_extend = 300
|
||||
else self.time_extend = 0 end
|
||||
end
|
||||
|
||||
if not self.grid:hasGemBlocks() or
|
||||
(self.stage_frames >= 3600 and self.current_map <= 20) then
|
||||
self.lcd = 0
|
||||
self.are = 0
|
||||
if self.stage_frames >= 3600 then self.time_extend = 0 end
|
||||
self.piece = nil
|
||||
|
||||
-- transition to next map
|
||||
if self.cleared_frames > 0 then
|
||||
self.cleared_frames = self.cleared_frames - 1
|
||||
if self.time_extend > 0 then
|
||||
self.time_limit = self.time_limit + 3
|
||||
self.time_extend = self.time_extend - 3
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
self.hold_queue = nil
|
||||
if self.current_map > 20 or (self.stage_frames < 3600 and self.current_map <= 20) then self.maps_cleared = self.maps_cleared + 1 end
|
||||
self.stage_frames = -1
|
||||
self.level = 0
|
||||
self.grid:clear()
|
||||
if (self.current_map == 20) then self.map_20_time = self.frames end
|
||||
if self.current_map >= 20 and self:checkRequirements() then
|
||||
self.clear = true
|
||||
self.completed = true
|
||||
return false
|
||||
else
|
||||
self.current_map = self.current_map + 1
|
||||
self.big_mode = effects[self.current_map] == "big"
|
||||
self.ready_frames = 100
|
||||
self.stage_pieces = 0
|
||||
self.grid:applyMap(maps[self.current_map])
|
||||
end
|
||||
|
||||
-- this is necessary to fix timer
|
||||
self.frames = self.frames - 1
|
||||
self.time_limit = self.time_limit + 1
|
||||
end
|
||||
|
||||
self.frames = self.frames + 1
|
||||
self.stage_frames = self.stage_frames + 1
|
||||
self.time_limit = self.time_limit - 1
|
||||
if self.time_limit <= 0 then self.game_over = true end
|
||||
|
||||
if self.piece ~= nil and self.frames % 30 == 0 and
|
||||
effects[self.current_map] == "roll"
|
||||
then
|
||||
ruleset:attemptRotate(
|
||||
{[config.gamesettings.world_reverse == 3 or
|
||||
(ruleset.world and config.gamesettings.world_reverse == 2)
|
||||
and "rotate_left" or "rotate_right"] = true},
|
||||
self.piece, self.grid, false
|
||||
)
|
||||
end
|
||||
else
|
||||
self.cleared_frames = STAGE_TRANSITION_TIME
|
||||
if not self.prev_inputs.hold and inputs.hold then
|
||||
self.hold_queue = table.remove(self.next_queue, 1)
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function colourXRay(game, block, x, y, age)
|
||||
local r, g, b, a = .5,.5,.5
|
||||
if ((game.stage_frames/2 - x) % 30 < 1)
|
||||
or game.stage_frames == 0
|
||||
or game.cleared_frames ~= STAGE_TRANSITION_TIME
|
||||
or game.stage_pieces % 2 == 0
|
||||
then
|
||||
a = 1
|
||||
else
|
||||
a = 1 - age / 4
|
||||
end
|
||||
return r, g, b, a, a
|
||||
end
|
||||
|
||||
local function colourColor(game, block, x, y, age)
|
||||
local r, g, b, a = .5,.5,.5
|
||||
if game.stage_frames == 0 or game.cleared_frames ~= STAGE_TRANSITION_TIME then
|
||||
a = 1
|
||||
else
|
||||
a = (game.stage_frames/30 + (y + math.abs(x-5.5))/5) % 1
|
||||
end
|
||||
return r, g, b, a, 0
|
||||
end
|
||||
|
||||
function SakuraGame:drawGrid()
|
||||
if effects[self.current_map] == "xray" then
|
||||
self.grid:drawCustom(colourXRay, self)
|
||||
elseif effects[self.current_map] == "color" then
|
||||
self.grid:drawCustom(colourColor, self)
|
||||
else
|
||||
self.grid:draw()
|
||||
-- if self.piece ~= nil and self.level < 100 then
|
||||
self:drawGhostPiece(ruleset)
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
||||
function SakuraGame:drawScoringInfo()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("STAGE", 240, 120, 80, "left")
|
||||
love.graphics.printf("TIME LIMIT", 240, 180, 80, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
if self.current_map <= 20 then
|
||||
love.graphics.printf("STAGE LIMIT", 240, 240, 100, "left")
|
||||
end
|
||||
if effects[self.current_map] then
|
||||
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.setColor(
|
||||
(self.time_limit % 4 < 2 and
|
||||
self.time_limit <= frameTime(0,10) and
|
||||
self.grid:hasGemBlocks() and
|
||||
self.time_limit ~= 0) and
|
||||
{ 1, 0.3, 0.3, 1 } or
|
||||
{ 1, 1, 1, 1 }
|
||||
)
|
||||
love.graphics.printf(formatTime(self.time_limit), 240, 200, 120, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
if self.current_map <= 20 then
|
||||
love.graphics.printf(formatTime(3600 - self.stage_frames), 240, 260, 120, "left")
|
||||
end
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(math.floor((self.level + 100) / 100) * 100, 240, 370, 40, "right")
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
love.graphics.printf(self.current_map, 290, 110, 80, "left")
|
||||
end
|
||||
|
||||
function SakuraGame:drawCustom()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
if self.ready_frames ~= 0 and not self.clear then
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.printf("STAGE " .. self.current_map, 64, 170, 160, "center")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if effects[self.current_map] then
|
||||
love.graphics.printf("EFFECT: " .. effects[self.current_map], 64, 270, 160, "center")
|
||||
end
|
||||
end
|
||||
|
||||
if self.cleared_frames > 0 and
|
||||
(not self.grid:hasGemBlocks() or
|
||||
(self.stage_frames >= 3600 and self.current_map <= 20)) then
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.printf("TIME LIMIT", 64, 180, 160, "center")
|
||||
love.graphics.printf("TIME EXTEND", 64, 240, 160, "center")
|
||||
love.graphics.printf("STAGE TIME", 64, 300, 160, "center")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf("STAGE " .. self.current_map, 64, 100, 160, "center")
|
||||
love.graphics.setColor(
|
||||
self.cleared_frames % 4 < 2 and
|
||||
{ 1, 1, 0.3, 1 } or
|
||||
{ 1, 1, 1, 1 }
|
||||
)
|
||||
love.graphics.printf(formatTime(self.time_limit), 64, 200, 160, "center")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(formatTime(self.time_extend), 64, 260, 160, "center")
|
||||
love.graphics.printf(formatTime(self.stage_frames), 64, 320, 160, "center")
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.printf((self.stage_frames >= 3600 and self.current_map <= 20) and "" or "CLEAR!", 64, 130, 160, "center")
|
||||
end
|
||||
|
||||
if self.clear then
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf("EXCELLENT!", 64, 180, 160, "center")
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
if self.current_map ~= 27 then
|
||||
love.graphics.printf("...but let's go\nbetter next time", 64, 220, 160, "center")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SakuraGame:getBackground()
|
||||
return (self.current_map - 1) % 20
|
||||
end
|
||||
|
||||
function SakuraGame:getHighscoreData()
|
||||
return {
|
||||
maps = self.maps_cleared,
|
||||
current_map = self.current_map,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
return SakuraGame
|
||||
@@ -52,23 +52,14 @@ function StrategyGame:getLineClearDelay()
|
||||
end
|
||||
|
||||
function StrategyGame:getLockDelay()
|
||||
if self.level < 500 then return 8
|
||||
elseif self.level < 700 then return 6
|
||||
else return 4 end
|
||||
if self.level < 700 then return 8
|
||||
else return 6 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
|
||||
|
||||
@@ -94,12 +94,8 @@ 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(),
|
||||
}
|
||||
function Survival2020Game:getSkin()
|
||||
return self.level >= 1000 and "bone" or "2tie"
|
||||
end
|
||||
|
||||
function Survival2020Game:hitTorikan(old_level, new_level)
|
||||
|
||||
@@ -111,9 +111,8 @@ function SurvivalA1Game:onLineClear(cleared_row_count)
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if new_level == 999 then
|
||||
self.clear = true
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -92,12 +92,8 @@ function SurvivalA3Game:getGarbageLimit()
|
||||
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(),
|
||||
}
|
||||
function SurvivalA3Game:getSkin()
|
||||
return self.level >= 1000 and "bone" or "2tie"
|
||||
end
|
||||
|
||||
function SurvivalA3Game:hitTorikan(old_level, new_level)
|
||||
|
||||
@@ -5,17 +5,16 @@ local Piece = require 'tetris.components.piece'
|
||||
|
||||
local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart'
|
||||
|
||||
local MarathonAX4Game = GameMode:extend()
|
||||
local SurvivalAXGame = GameMode:extend()
|
||||
|
||||
MarathonAX4Game.name = "Marathon AX4"
|
||||
MarathonAX4Game.hash = "MarathonAX4"
|
||||
MarathonAX4Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
SurvivalAXGame.name = "Survival AX"
|
||||
SurvivalAXGame.hash = "SurvivalAX"
|
||||
SurvivalAXGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function MarathonAX4Game:new()
|
||||
MarathonAX4Game.super:new()
|
||||
function SurvivalAXGame:new()
|
||||
SurvivalAXGame.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = Bag7NoSZOStartRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
@@ -29,7 +28,7 @@ function MarathonAX4Game:new()
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getARE()
|
||||
function SurvivalAXGame:getARE()
|
||||
if self.lines < 10 then return 18
|
||||
elseif self.lines < 40 then return 14
|
||||
elseif self.lines < 60 then return 12
|
||||
@@ -39,24 +38,24 @@ function MarathonAX4Game:getARE()
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getLineARE()
|
||||
function SurvivalAXGame:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getDasLimit()
|
||||
function SurvivalAXGame: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 MarathonAX4Game:getLineClearDelay()
|
||||
function SurvivalAXGame:getLineClearDelay()
|
||||
if self.lines < 10 then return 14
|
||||
elseif self.lines < 30 then return 9
|
||||
else return 5 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getLockDelay()
|
||||
function SurvivalAXGame:getLockDelay()
|
||||
if self.lines < 10 then return 28
|
||||
elseif self.lines < 20 then return 24
|
||||
elseif self.lines < 30 then return 22
|
||||
@@ -66,23 +65,16 @@ function MarathonAX4Game:getLockDelay()
|
||||
else return 13 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getGravity()
|
||||
function SurvivalAXGame:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSection()
|
||||
function SurvivalAXGame:getSection()
|
||||
return math.floor(level / 100) + 1
|
||||
end
|
||||
|
||||
function MarathonAX4Game: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
|
||||
function SurvivalAXGame:advanceOneFrame()
|
||||
if self.ready_frames == 0 then
|
||||
if not self.section_clear then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
@@ -93,7 +85,7 @@ function MarathonAX4Game:advanceOneFrame()
|
||||
return true
|
||||
end
|
||||
|
||||
function MarathonAX4Game:onLineClear(cleared_row_count)
|
||||
function SurvivalAXGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_lines = self.lines + cleared_row_count
|
||||
self:updateSectionTimes(self.lines, new_lines)
|
||||
@@ -101,16 +93,16 @@ function MarathonAX4Game:onLineClear(cleared_row_count)
|
||||
if self.lines == 150 then
|
||||
self.grid:clear()
|
||||
self.clear = true
|
||||
self.roll_frames = -150
|
||||
self.completed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSectionTime()
|
||||
function SurvivalAXGame:getSectionTime()
|
||||
return self.frames - self.section_start_time
|
||||
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
|
||||
-- record new section
|
||||
table.insert(self.section_times, self:getSectionTime())
|
||||
@@ -119,23 +111,23 @@ function MarathonAX4Game:updateSectionTimes(old_lines, new_lines)
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:onPieceEnter()
|
||||
function SurvivalAXGame:onPieceEnter()
|
||||
self.section_clear = false
|
||||
end
|
||||
|
||||
function MarathonAX4Game:drawGrid(ruleset)
|
||||
function SurvivalAXGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getHighscoreData()
|
||||
function SurvivalAXGame:getHighscoreData()
|
||||
return {
|
||||
lines = self.lines,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function MarathonAX4Game:drawScoringInfo()
|
||||
MarathonAX4Game.super.drawScoringInfo(self)
|
||||
function SurvivalAXGame:drawScoringInfo()
|
||||
SurvivalAXGame.super.drawScoringInfo(self)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -165,12 +157,12 @@ function MarathonAX4Game:drawScoringInfo()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSectionEndLines()
|
||||
function SurvivalAXGame:getSectionEndLines()
|
||||
return math.floor(self.lines / 10 + 1) * 10
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getBackground()
|
||||
function SurvivalAXGame:getBackground()
|
||||
return math.floor(self.lines / 10)
|
||||
end
|
||||
|
||||
return MarathonAX4Game
|
||||
return SurvivalAXGame
|
||||
24
tetris/randomizers/bag.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local BagRandomizer = Randomizer:extend()
|
||||
|
||||
function BagRandomizer:new(pieces)
|
||||
self.bag = {}
|
||||
self.possible_pieces = pieces
|
||||
self.pieces = pieces
|
||||
for i = 1, self.pieces do
|
||||
table.insert(self.bag, i)
|
||||
end
|
||||
end
|
||||
|
||||
function BagRandomizer:generatePiece()
|
||||
if next(self.bag) == nil then
|
||||
for i = 1, self.pieces do
|
||||
table.insert(self.bag, i)
|
||||
end
|
||||
end
|
||||
local x = math.random(table.getn(self.bag))
|
||||
return table.remove(self.bag, x)
|
||||
end
|
||||
|
||||
return BagRandomizer
|
||||
@@ -28,11 +28,8 @@ end
|
||||
function History6Rolls35PoolRandomizer:generatePiece()
|
||||
local index, x
|
||||
if self.first then
|
||||
local prevent = {"S", "Z", "O"}
|
||||
repeat
|
||||
index = math.random(#self.pool)
|
||||
x = self.pool[index]
|
||||
until not inHistory(x, prevent)
|
||||
index = math.random(20)
|
||||
x = self.pool[index]
|
||||
self.first = false
|
||||
else
|
||||
for i = 1, 6 do
|
||||
|
||||
@@ -3,6 +3,7 @@ local Object = require 'libs.classic'
|
||||
local Randomizer = Object:extend()
|
||||
|
||||
function Randomizer:new()
|
||||
self.possible_pieces = 7
|
||||
self:initialize()
|
||||
end
|
||||
|
||||
|
||||
@@ -110,13 +110,7 @@ function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue()
|
||||
if config.gamesettings.world_reverse == 3 then
|
||||
return 1
|
||||
else
|
||||
return 3
|
||||
end
|
||||
end
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.arika_ti'
|
||||
local Ruleset = require 'tetris.rulesets.arika_ace2'
|
||||
|
||||
local ARS = Ruleset:extend()
|
||||
|
||||
@@ -19,54 +19,4 @@ ARS.colourscheme = {
|
||||
ARS.softdrop_lock = false
|
||||
ARS.harddrop_lock = true
|
||||
|
||||
ARS.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 },
|
||||
}
|
||||
|
||||
ARS.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 },
|
||||
}
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
end
|
||||
|
||||
function ARS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 128 then
|
||||
piece:dropToBottom(grid)
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 128 then
|
||||
piece:dropToBottom(grid)
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
if piece.floorkick >= 1 then
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
end
|
||||
end
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -5,32 +5,86 @@ local ARS = Ruleset:extend()
|
||||
|
||||
ARS.name = "ACE-ARS2"
|
||||
ARS.hash = "ArikaACE2"
|
||||
ARS.spawn_above_field = true
|
||||
|
||||
ARS.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 },
|
||||
}
|
||||
function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
ARS.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 },
|
||||
}
|
||||
-- O doesn't kick
|
||||
if (piece.shape == "O") then return end
|
||||
|
||||
-- center column rule
|
||||
if (
|
||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||
) and (
|
||||
piece.rotation == 0 or piece.rotation == 2
|
||||
) then
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
if offset.x == 0 then
|
||||
return
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if piece.shape == "I" then
|
||||
-- special kick rules for I
|
||||
if (new_piece.rotation == 0 or new_piece.rotation == 2) and
|
||||
(piece:isMoveBlocked(grid, {x=-1, y=0}) or piece:isMoveBlocked(grid, {x=1, y=0})) then
|
||||
-- kick right, right2, left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) then
|
||||
-- kick up, up2
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- kick right, kick left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif piece.shape == "T"
|
||||
and new_piece.rotation == 0
|
||||
and piece:isDropBlocked(grid)
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
-- T floorkick
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
end
|
||||
|
||||
function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0
|
||||
end
|
||||
|
||||
function ARS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
@@ -51,9 +105,10 @@ function ARS:onPieceRotate(piece, grid)
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
if piece.floorkick >= 1 then
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -4,38 +4,35 @@ local Ruleset = require 'tetris.rulesets.ti_srs'
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
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.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 },
|
||||
}
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
function SRS:onPieceRotate(piece, grid, upward)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
if upward or piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= self.MANIPULATIONS_MAX then
|
||||
if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:canPieceRotate(piece)
|
||||
return piece.manipulations < self.MANIPULATIONS_MAX
|
||||
end
|
||||
|
||||
return SRS
|
||||
|
||||
@@ -38,35 +38,35 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
(piece:isMoveBlocked(grid, {x=-1, y=0}) or piece:isMoveBlocked(grid, {x=1, y=0})) then
|
||||
-- kick right, right2, left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then
|
||||
-- kick up, up2
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
piece.floorkick = 1
|
||||
self:onPieceRotate(piece, grid, true)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
|
||||
piece.floorkick = 1
|
||||
self:onPieceRotate(piece, grid, true)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- kick right, kick left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif piece.shape == "T"
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
@@ -75,8 +75,8 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
then
|
||||
-- T floorkick
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
self:onPieceRotate(piece, grid, true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -93,10 +93,16 @@ function ARS:onPieceDrop(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:onPieceRotate(piece, grid)
|
||||
if piece.floorkick >= 1 then
|
||||
function ARS:onPieceRotate(piece, grid, floorkick)
|
||||
if piece.floorkick >= 2 and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
elseif piece.floorkick >= 1 and not floorkick then
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -364,9 +364,9 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial)
|
||||
|
||||
if rot_dir == 0 then return end
|
||||
|
||||
if self.world and config.gamesettings.world_reverse == 2 then
|
||||
rot_dir = 4 - rot_dir
|
||||
end
|
||||
if config.gamesettings.world_reverse == 3 or (self.world and config.gamesettings.world_reverse == 2) then
|
||||
rot_dir = 4 - rot_dir
|
||||
end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
self:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
@@ -384,9 +384,9 @@ function CRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
for idx, offset in pairs(kicks) do
|
||||
kicked_piece = new_piece:withOffset(offset)
|
||||
if grid:canPlacePiece(kicked_piece) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,61 +22,43 @@ Ruleset.harddrop_lock = false
|
||||
|
||||
Ruleset.enable_IRS_wallkicks = false
|
||||
Ruleset.are_cancel = false
|
||||
Ruleset.are = true
|
||||
Ruleset.spawn_above_field = false
|
||||
|
||||
Ruleset.next_sounds = {
|
||||
I = "I",
|
||||
L = "L",
|
||||
J = "J",
|
||||
S = "S",
|
||||
Z = "Z",
|
||||
O = "O",
|
||||
T = "T"
|
||||
}
|
||||
|
||||
Ruleset.pieces = 7
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function Ruleset:new()
|
||||
function Ruleset:new(game_mode)
|
||||
self.game = game_mode
|
||||
local bones
|
||||
if config.gamesettings.piece_colour == 1 then
|
||||
blocks["bone"] = (not self.world) and
|
||||
{
|
||||
R = love.graphics.newImage("res/img/bone.png"),
|
||||
O = love.graphics.newImage("res/img/bone.png"),
|
||||
Y = love.graphics.newImage("res/img/bone.png"),
|
||||
G = love.graphics.newImage("res/img/bone.png"),
|
||||
C = love.graphics.newImage("res/img/bone.png"),
|
||||
B = love.graphics.newImage("res/img/bone.png"),
|
||||
M = love.graphics.newImage("res/img/bone.png"),
|
||||
F = love.graphics.newImage("res/img/bone.png"),
|
||||
A = love.graphics.newImage("res/img/bone.png"),
|
||||
X = love.graphics.newImage("res/img/bone.png"),
|
||||
} or {
|
||||
R = love.graphics.newImage("res/img/bonew.png"),
|
||||
O = love.graphics.newImage("res/img/bonew.png"),
|
||||
Y = love.graphics.newImage("res/img/bonew.png"),
|
||||
G = love.graphics.newImage("res/img/bonew.png"),
|
||||
C = love.graphics.newImage("res/img/bonew.png"),
|
||||
B = love.graphics.newImage("res/img/bonew.png"),
|
||||
M = love.graphics.newImage("res/img/bonew.png"),
|
||||
F = love.graphics.newImage("res/img/bonew.png"),
|
||||
A = love.graphics.newImage("res/img/bonew.png"),
|
||||
X = love.graphics.newImage("res/img/bonew.png"),
|
||||
}
|
||||
bones = self.world and "w" or ""
|
||||
else
|
||||
blocks["bone"] = (config.gamesettings.piece_colour == 2) and
|
||||
{
|
||||
R = love.graphics.newImage("res/img/bone.png"),
|
||||
O = love.graphics.newImage("res/img/bone.png"),
|
||||
Y = love.graphics.newImage("res/img/bone.png"),
|
||||
G = love.graphics.newImage("res/img/bone.png"),
|
||||
C = love.graphics.newImage("res/img/bone.png"),
|
||||
B = love.graphics.newImage("res/img/bone.png"),
|
||||
M = love.graphics.newImage("res/img/bone.png"),
|
||||
F = love.graphics.newImage("res/img/bone.png"),
|
||||
A = love.graphics.newImage("res/img/bone.png"),
|
||||
X = love.graphics.newImage("res/img/bone.png"),
|
||||
} or {
|
||||
R = love.graphics.newImage("res/img/bonew.png"),
|
||||
O = love.graphics.newImage("res/img/bonew.png"),
|
||||
Y = love.graphics.newImage("res/img/bonew.png"),
|
||||
G = love.graphics.newImage("res/img/bonew.png"),
|
||||
C = love.graphics.newImage("res/img/bonew.png"),
|
||||
B = love.graphics.newImage("res/img/bonew.png"),
|
||||
M = love.graphics.newImage("res/img/bonew.png"),
|
||||
F = love.graphics.newImage("res/img/bonew.png"),
|
||||
A = love.graphics.newImage("res/img/bonew.png"),
|
||||
X = love.graphics.newImage("res/img/bonew.png"),
|
||||
}
|
||||
bones = config.gamesettings.piece_colour == 3 and "w" or ""
|
||||
end
|
||||
blocks.bone = {
|
||||
R = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
O = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
Y = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
G = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
C = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
B = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
M = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
F = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
A = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
X = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
}
|
||||
end
|
||||
|
||||
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
@@ -88,7 +70,15 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
end
|
||||
end
|
||||
|
||||
self:attemptRotate(new_inputs, piece, grid, initial)
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
|
||||
if self:canPieceRotate(piece, grid) then
|
||||
self:attemptRotate(new_inputs, piece, grid, initial)
|
||||
end
|
||||
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
|
||||
-- prev_inputs becomes the previous inputs
|
||||
for input, value in pairs(inputs) do
|
||||
@@ -129,54 +119,93 @@ function Ruleset:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
end
|
||||
|
||||
function Ruleset:movePiece(piece, grid, move, instant)
|
||||
local x = piece.position.x
|
||||
if not self:canPieceMove(piece, grid) then return end
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
local offset = ({x=0, y=0})
|
||||
local moves = 0
|
||||
local y = piece.position.y
|
||||
if move == "left" then
|
||||
piece:moveInGrid({x=-1, y=0}, 1, grid, false)
|
||||
offset.x = -1
|
||||
moves = 1
|
||||
elseif move == "right" then
|
||||
piece:moveInGrid({x=1, y=0}, 1, grid, false)
|
||||
offset.x = 1
|
||||
moves = 1
|
||||
elseif move == "speedleft" then
|
||||
piece:moveInGrid({x=-1, y=0}, 10, grid, instant)
|
||||
offset.x = -1
|
||||
moves = grid.width
|
||||
elseif move == "speedright" then
|
||||
piece:moveInGrid({x=1, y=0}, 10, grid, instant)
|
||||
offset.x = 1
|
||||
moves = grid.width
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
for i = 1, moves do
|
||||
local x = piece.position.x
|
||||
if moves ~= 1 then
|
||||
piece:moveInGrid(offset, 1, grid, instant)
|
||||
else
|
||||
piece:moveInGrid(offset, 1, grid, false)
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
if piece.locked then break end
|
||||
end
|
||||
end
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
if instant and piece.position.y ~= y then
|
||||
self:onPieceDrop(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function Ruleset:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
if piece.big then
|
||||
gravity = gravity / 2
|
||||
drop_speed = drop_speed / 2
|
||||
end
|
||||
|
||||
local y = piece.position.y
|
||||
if inputs["down"] == true and drop_locked == false then
|
||||
if additive_gravity then
|
||||
piece:addGravity(gravity + drop_speed, grid)
|
||||
piece:addGravity(gravity + drop_speed, grid, classic_lock)
|
||||
else
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid)
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
|
||||
end
|
||||
elseif inputs["up"] == true and hard_drop_enabled == true then
|
||||
if hard_drop_locked == true or piece:isDropBlocked(grid) then
|
||||
piece:addGravity(gravity, grid)
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
else
|
||||
piece:dropToBottom(grid)
|
||||
end
|
||||
else
|
||||
piece:addGravity(gravity, grid)
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
end
|
||||
if piece.position.y ~= y then
|
||||
self:onPieceDrop(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function Ruleset:lockPiece(piece, grid, lock_delay)
|
||||
if piece:isDropBlocked(grid) and piece.gravity >= 1 and piece.lock_delay >= lock_delay then
|
||||
function Ruleset:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
function Ruleset:get180RotationValue() return 2 end
|
||||
function Ruleset:getDefaultOrientation() return 1 end
|
||||
function Ruleset:getDrawOffset(shape, orientation) return { x=0, y=0 } end
|
||||
function Ruleset:getAboveFieldOffset(shape, orientation)
|
||||
if shape == "I" then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
||||
function Ruleset:initializePiece(
|
||||
inputs, data, grid, gravity, prev_inputs,
|
||||
@@ -189,21 +218,49 @@ function Ruleset:initializePiece(
|
||||
else
|
||||
spawn_positions = self.spawn_positions
|
||||
end
|
||||
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
|
||||
local colours
|
||||
if self.pieces == 7 then
|
||||
colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
else
|
||||
colours = self.colourscheme
|
||||
end
|
||||
|
||||
local spawn_x
|
||||
if (grid.width ~= 10) then
|
||||
local percent = spawn_positions[data.shape].x / 10
|
||||
for i = grid.width - 1, 0, -1 do
|
||||
if i / grid.width <= percent then
|
||||
spawn_x = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local spawn_dy
|
||||
if (config.gamesettings.spawn_positions == 1) then
|
||||
spawn_dy = (
|
||||
self.spawn_above_field and
|
||||
self:getAboveFieldOffset(data.shape, data.orientation) or 0
|
||||
)
|
||||
else
|
||||
spawn_dy = (
|
||||
config.gamesettings.spawn_positions == 3 and
|
||||
self:getAboveFieldOffset(data.shape, data.orientation) or 0
|
||||
)
|
||||
end
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y
|
||||
x = spawn_x or spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y - spawn_dy
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
if irs then
|
||||
if inputs.rotate_left or inputs.rotate_left2 or
|
||||
inputs.rotate_right or inputs.rotate_right2 or
|
||||
inputs.rotate_180 then
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
if (data.orientation - 1) ~= piece.rotation then
|
||||
playSE("irs")
|
||||
end
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
end
|
||||
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
|
||||
return piece
|
||||
@@ -216,8 +273,9 @@ function Ruleset:processPiece(
|
||||
inputs, piece, grid, gravity, prev_inputs,
|
||||
move, lock_delay, drop_speed,
|
||||
drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
if piece.locked then return end
|
||||
|
||||
local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed]
|
||||
|
||||
@@ -230,11 +288,13 @@ function Ruleset:processPiece(
|
||||
end
|
||||
self:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
self:lockPiece(piece, grid, lock_delay)
|
||||
self:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
end
|
||||
|
||||
function Ruleset:canPieceMove(piece, grid) return true end
|
||||
function Ruleset:canPieceRotate(piece, grid) return true end
|
||||
function Ruleset:onPieceMove(piece) end
|
||||
function Ruleset:onPieceRotate(piece) end
|
||||
function Ruleset:onPieceDrop(piece) end
|
||||
|
||||
105
tetris/rulesets/standard.lua
Normal file
@@ -0,0 +1,105 @@
|
||||
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.manipulations >= self.MANIPULATIONS_MAX then
|
||||
piece:moveInGrid({ x = 0, y = 1 }, 1, grid)
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:canPieceRotate() return true end
|
||||
|
||||
return SRS
|
||||
@@ -3,46 +3,37 @@ local Ruleset = require 'tetris.rulesets.arika_srs'
|
||||
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
SRS.name = "Guideline SRS"
|
||||
SRS.hash = "Standard"
|
||||
SRS.name = "SRS-X"
|
||||
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.MANIPULATIONS_MAX = 15
|
||||
SRS.MANIPULATIONS_MAX = 24
|
||||
SRS.ROTATIONS_MAX = 12
|
||||
|
||||
function SRS:check_new_low(piece)
|
||||
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.manipulations = 0
|
||||
--piece.rotations = 0
|
||||
piece.lowest_y = y
|
||||
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.
|
||||
|
||||
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
@@ -63,7 +54,7 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
if grid:canPlacePiece(kicked_piece) then
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
self:onPieceRotate(piece, grid, offset.y < 0)
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -72,12 +63,16 @@ end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.manipulations = 0
|
||||
piece.rotations = 0
|
||||
piece.lowest_y = -math.huge
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
self:check_new_low(piece)
|
||||
if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then
|
||||
self:checkNewLow(piece)
|
||||
if (
|
||||
piece.manipulations >= self.MANIPULATIONS_MAX or
|
||||
piece.rotations >= self.ROTATIONS_MAX
|
||||
) and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
else
|
||||
piece.lock_delay = 0 -- step reset
|
||||
@@ -88,22 +83,24 @@ function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= self.MANIPULATIONS_MAX then
|
||||
if piece.manipulations >= SRS.MANIPULATIONS_MAX then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
function SRS:onPieceRotate(piece, grid, upward)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
self:check_new_low(piece)
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= self.MANIPULATIONS_MAX then
|
||||
piece:moveInGrid({ x = 0, y = 1 }, 1, grid)
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
if upward or piece:isDropBlocked(grid) then
|
||||
piece.rotations = piece.rotations + 1
|
||||
if piece.rotations >= self.ROTATIONS_MAX and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:canPieceRotate(piece)
|
||||
return piece.rotations < self.ROTATIONS_MAX
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 2 end
|
||||
|
||||
@@ -4,7 +4,7 @@ local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
SRS.name = "Ti-World"
|
||||
SRS.hash = "Bad I-kicks"
|
||||
SRS.hash = "StandardTI"
|
||||
SRS.world = true
|
||||
SRS.colourscheme = {
|
||||
I = "C",
|
||||
@@ -89,22 +89,22 @@ SRS.block_offsets = {
|
||||
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}},
|
||||
[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}},
|
||||
},
|
||||
[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}},
|
||||
[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]={
|
||||
[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}},
|
||||
[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}},
|
||||
[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}},
|
||||
},
|
||||
};
|
||||
@@ -112,22 +112,22 @@ SRS.wallkicks_3x3 = {
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[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}},
|
||||
},
|
||||
[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]={},
|
||||
[3]={{x=0,y=1},{x=0,y=2},{x=0,y=-1},{x=0,y=-2},{x=-1,y=0}},
|
||||
},
|
||||
[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}},
|
||||
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}},
|
||||
[1]={},
|
||||
[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}},
|
||||
},
|
||||
};
|
||||
@@ -150,9 +150,9 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
for idx, offset in pairs(kicks) do
|
||||
kicked_piece = new_piece:withOffset(offset)
|
||||
if grid:canPlacePiece(kicked_piece) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid, offset.y < 0)
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -182,22 +182,20 @@ function SRS:onPieceMove(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
function SRS:onPieceRotate(piece, grid, upward)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
if upward or piece:isDropBlocked(grid) then
|
||||
piece.rotations = piece.rotations + 1
|
||||
if piece.rotations >= self.ROTATIONS_MAX then
|
||||
if piece.rotations >= self.ROTATIONS_MAX and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue()
|
||||
if config.gamesettings.world_reverse == 1 then
|
||||
return 1
|
||||
else
|
||||
return 3
|
||||
end
|
||||
function SRS:canPieceRotate(piece)
|
||||
return piece.rotations < self.ROTATIONS_MAX
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 3 end
|
||||
|
||||
return SRS
|
||||
|
||||