mirror of
https://github.com/SashLilac/cambridge.git
synced 2025-05-13 20:21:25 -05:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9982613e26 | ||
|
|
4cb20101b0 | ||
|
|
63c0721978 | ||
|
|
1366451a3d | ||
|
|
6b624b9853 | ||
|
|
b0bda25466 | ||
|
|
2bde9d1378 | ||
|
|
6178b2cee9 | ||
|
|
4e8a237de3 | ||
|
|
5606251ea7 | ||
|
|
a4984fd687 | ||
|
|
b7ef7d1976 | ||
|
|
293b7398a2 | ||
|
|
8a2237a77c | ||
|
|
bdad32ac79 | ||
|
|
3cc918841f | ||
|
|
5f7ea0648e | ||
|
|
5d34218b97 | ||
|
|
fcd8b0f360 | ||
|
|
b983e1c108 | ||
|
|
36ab451b70 | ||
|
|
8fef7faa6a | ||
|
|
f13e2096b2 | ||
|
|
6b7f18d58a | ||
|
|
d5ce2ee9ba | ||
|
|
f04b57e7eb | ||
|
|
be644bf57b | ||
|
|
9d15feef33 | ||
|
|
634a5bc03b | ||
|
|
f1ad1f0ea4 | ||
|
|
a534331b11 | ||
|
|
d602fdfc7e | ||
|
|
971151e210 | ||
|
|
593cad0e71 | ||
|
|
1254de15d5 | ||
|
|
5c5ffc6887 | ||
|
|
5131061e42 | ||
|
|
209e60e82e | ||
|
|
c4ba80b60d | ||
|
|
2ba957f65a | ||
|
|
321de8564c | ||
|
|
96ac054cf6 | ||
|
|
b7fc51f4bd | ||
|
|
04ccd628be | ||
|
|
704e6dae55 | ||
|
|
19d1686b9d | ||
|
|
aa01479c12 | ||
|
|
5a1b137c2a | ||
|
|
956e826bb2 | ||
|
|
cebe57dd1e | ||
|
|
1db0a0ef05 | ||
|
|
0e7a2bb9fe | ||
|
|
31b0f60475 | ||
|
|
1d6ac62d7d | ||
|
|
b316d9617b | ||
|
|
99f7ff72fd | ||
|
|
df489a2603 | ||
|
|
4105040334 |
102
CONTRIBUTING.md
102
CONTRIBUTING.md
@@ -1,13 +1,3 @@
|
|||||||
Coding conventions
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Use tabs to indent, spaces to align.
|
|
||||||
* Specifically, spaces should not appear at the beginning of a line, and tabs should not appear _except_ at the beginning of a line.
|
|
||||||
* The sole exception is in a multiline `if` statement; the initial `if` should have four spaces before it to align it with an `elseif` on the next line.
|
|
||||||
* Comments with lines at the end of code must be one line long. Multi-line comments must appeare in their own block.
|
|
||||||
* Use `snake_case` for variables, `camelCase` for functions.
|
|
||||||
|
|
||||||
|
|
||||||
Contributor's License Agreement
|
Contributor's License Agreement
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
@@ -15,4 +5,94 @@ By contributing source code or other assets (e.g. music, artwork, graphics) to C
|
|||||||
|
|
||||||
You also waive all moral rights to your contributions insofar as they are used in the Cambridge repository or in any code or works deriving therefrom.
|
You also waive all moral rights to your contributions insofar as they are used in the Cambridge repository or in any code or works deriving therefrom.
|
||||||
|
|
||||||
(Regarding the above clause, I will still make my best effort to provide sufficient attribution to all contributions. At the very least you'll get documentation of your contributions under SOURCES, and probably a special place in the credit roll as well.)
|
(Notwithstanding the above clause, I will still make my best effort to provide sufficient attribution to all contributions. At the very least you'll get documentation of your contributions under SOURCES, and probably a special place in the credit roll as well.)
|
||||||
|
|
||||||
|
|
||||||
|
Git / Repo conventions
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
In general, use `kebab-case` for branch names. Also, make sure they're concise and descriptive - like 2 or 3 words is usually good.
|
||||||
|
|
||||||
|
```
|
||||||
|
* badbeef (badBranchName) This branch name is bad.
|
||||||
|
| * defaced (another_bad_branch_name) This branch name is also bad because it uses snake case.
|
||||||
|
|/
|
||||||
|
| * deadcab (generic) This branch name isn't very descriptive.
|
||||||
|
|/
|
||||||
|
| * bac0040 (this-long-winded-branch-name-that-could-be-its-own-commit-message) Self-explanatory.
|
||||||
|
|/
|
||||||
|
| * 600db01 (good-branch-name) This branch name is good.
|
||||||
|
|/
|
||||||
|
* 0000420 (HEAD -> master, tag: v0.6.9) This is a sexy root commit.
|
||||||
|
```
|
||||||
|
|
||||||
|
The top line of a commit message should generally be one full sentence long, without too many subordinate clauses. Don't sweat 50/72, but try not go over about 100 characters either.
|
||||||
|
* If the message starts with a verb, it should be written in the past tense, as a description of what the commit _did_ to the commit tree. (e.g. _Made_ a change, _Fixed_ a bug, _Added_ a feature)
|
||||||
|
* Alternatively, include a description (in the present tense) of what is now true thanks to this commit. (e.g. "The Puyo Puyo mode can now support up to 50 players.")
|
||||||
|
|
||||||
|
```
|
||||||
|
* 800000d (message-too-long) Made multiplayer stuff play well with the new v0.2.5 server by fixing a problem the client was having with sending multiple 4-KB packets within 2 milliseconds of each other.
|
||||||
|
| * defaced (not-descriptive-enough) Fixed stuff.
|
||||||
|
|/
|
||||||
|
| * bad0003 (present-tense) Lengthens the retry period of the server connection to 15 seconds.
|
||||||
|
|/
|
||||||
|
| * bad0004 (imperative-mood) Force the credit roll to end after 67 seconds if no input is detected.
|
||||||
|
|/
|
||||||
|
| * 600d001 (good-commit-summary) Made the Jenny Marathon mode not top out randomly at level 600.
|
||||||
|
| * 600d002 (also-good) Backgrounds don't suck anymore.
|
||||||
|
|/
|
||||||
|
* 1234567 (HEAD -> master, tag: v0.4.2) Updated docs in preparation for a new release.
|
||||||
|
```
|
||||||
|
|
||||||
|
When making pull requests, always include:
|
||||||
|
|
||||||
|
* A title that works well as a commit title, since that's what's going to appear when it's merged.
|
||||||
|
* A full description of the problem that the pull request solves or the feature that it implements.
|
||||||
|
* If the whole purpose of the pull request is to resolve a particular issue and nothing else, "Fixes #[issue number]" counts as a full description. Otherwise if there's anything else in the pull request, make a short note of "also [did this other thing]".
|
||||||
|
|
||||||
|
|
||||||
|
Coding conventions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Use tabs to indent, spaces to align.
|
||||||
|
|
||||||
|
* Specifically, spaces should not appear at the beginning of a line, and tabs should not appear _except_ at the beginning of a line.
|
||||||
|
* The sole exception is in a multiline `if` statement; the initial `if` should have four spaces before it to align it with an `elseif` on the next line. For example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
---- 4 spaces
|
||||||
|
if self.level < 900 then return 12
|
||||||
|
elseif self.level < 1200 then return 8
|
||||||
|
else return 6 end
|
||||||
|
```
|
||||||
|
|
||||||
|
Comments at the end of lines of code must be one line long. Multi-line comments must appear in their own block.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
if not self.piece:isDropBlocked(self.grid) then
|
||||||
|
-- this is a comment that appears in a block of its own, separate from any code
|
||||||
|
-- consecutive multiline comments must have the same indentation level and
|
||||||
|
-- not appear next on the same line as actual code
|
||||||
|
self.drop_bonus = 0 -- comments at the end of a line must stay on that line
|
||||||
|
else
|
||||||
|
if piece_dy >= 1 then -- basically
|
||||||
|
self.drop_bonus = self.drop_bonus + piece_dy * 20 -- this sort of
|
||||||
|
end -- multiline comment
|
||||||
|
self.drop_bonus = self.drop_bonus + 1 -- is completely
|
||||||
|
end -- unacceptable
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `snake_case` for variables, `camelCase` for functions.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function MyGameMode:on_activate_bleep_bloop()
|
||||||
|
-- no, bad, use "onActivateBleepBloop"
|
||||||
|
|
||||||
|
local bleepBloopFrames = 240
|
||||||
|
-- this is also bad, use "bleep_bloop_frames"
|
||||||
|
|
||||||
|
local bleep_bloop_bonus = self.lock_delay * 150
|
||||||
|
self.bleepBloopSubscore = self.bleepBloopSubscore + bleep_bloop_bonus
|
||||||
|
-- this should be self."bleep_bloop_subscore", member variables are also variables
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
The code in Cambridge is licensed under the MIT license.
|
Copyright (c) 2018-2019 Joe Zeng
|
||||||
|
|
||||||
Copyright (c) 2018 Joe Zeng
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -18,12 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Some code and assets in this repository are contributed by members of the
|
|
||||||
community, as well as borrowed from other places, either with licensing
|
|
||||||
or as placeholders until suitable material can be found that is properly
|
|
||||||
licensed. Their original sources, and copyright notices if applicable, are
|
|
||||||
listed in the file SOURCES.
|
|
||||||
64
README.md
Normal file
64
README.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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) and [Oshisaure](https://github.com/oshisaure)!
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for their amazing contributions to my life in general!
|
||||||
|
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||||
|
- [joezeng](https://github.com/joezeng) for the original project.
|
||||||
|
|
||||||
|
Installation instructions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Pre-built releases are available on the releases page.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Unzip the exe file and run it directly. All assets are currently bundled inside the executable.
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
For the time being, the file `cambridge.love` only works on the command line. Install `love` with [Homebrew](https://brew.sh), and run:
|
||||||
|
|
||||||
|
$ love cambridge.love
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
Same as macOS, except install `love` with your favourite package manager.
|
||||||
|
|
||||||
|
|
||||||
|
Running from source
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
If you want the bleeding-edge release, you can also clone the code straight from this repository.
|
||||||
|
|
||||||
|
### macOS, Linux
|
||||||
|
|
||||||
|
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!**
|
||||||
|
|
||||||
|
Clone the repository in git:
|
||||||
|
|
||||||
|
git clone https://github.com/SashLilac/cambridge
|
||||||
|
|
||||||
|
Then, navigate to the root directory that you just cloned, and type:
|
||||||
|
|
||||||
|
love .
|
||||||
|
|
||||||
|
It should run automatically!
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
The Cambridge project is licensed under the MIT license (included in LICENSE.md).
|
||||||
|
|
||||||
|
Some code and assets in this repository are contributed by members of the
|
||||||
|
community, as well as borrowed from other places, either with licensing
|
||||||
|
or as placeholders until suitable material can be found that is properly
|
||||||
|
licensed. Their original sources, and copyright notices if applicable, are
|
||||||
|
listed in the file SOURCES.
|
||||||
2
conf.lua
2
conf.lua
@@ -1,6 +1,8 @@
|
|||||||
function love.conf(t)
|
function love.conf(t)
|
||||||
t.identity = "cambridge"
|
t.identity = "cambridge"
|
||||||
|
|
||||||
|
t.console = true
|
||||||
|
|
||||||
t.window.title = "Cambridge"
|
t.window.title = "Cambridge"
|
||||||
t.window.width = 640
|
t.window.width = 640
|
||||||
t.window.height = 480
|
t.window.height = 480
|
||||||
|
|||||||
BIN
dist/win32/OpenAL32.dll
vendored
Normal file
BIN
dist/win32/OpenAL32.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/SDL2.dll
vendored
Normal file
BIN
dist/win32/SDL2.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/love.dll
vendored
Normal file
BIN
dist/win32/love.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/lua51.dll
vendored
Normal file
BIN
dist/win32/lua51.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/mpg123.dll
vendored
Normal file
BIN
dist/win32/mpg123.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/msvcp120.dll
vendored
Normal file
BIN
dist/win32/msvcp120.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win32/msvcr120.dll
vendored
Normal file
BIN
dist/win32/msvcr120.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/OpenAL32.dll
vendored
Normal file
BIN
dist/windows/OpenAL32.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/SDL2.dll
vendored
Normal file
BIN
dist/windows/SDL2.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/love.dll
vendored
Normal file
BIN
dist/windows/love.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/lua51.dll
vendored
Normal file
BIN
dist/windows/lua51.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/mpg123.dll
vendored
Normal file
BIN
dist/windows/mpg123.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/msvcp120.dll
vendored
Normal file
BIN
dist/windows/msvcp120.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/msvcr120.dll
vendored
Normal file
BIN
dist/windows/msvcr120.dll
vendored
Normal file
Binary file not shown.
@@ -1,23 +1,35 @@
|
|||||||
Game modes
|
Game modes
|
||||||
==========
|
==========
|
||||||
|
|
||||||
There are several classes of 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
|
MARATHON
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Modes in which the goal is to play as well as possible over a limited game interval, to ultimately achieve the title of Grand Master.
|
Modes in which the goal is to play as well as possible over a limited game interval.
|
||||||
|
|
||||||
* **MARATHON**: Get to level 999!
|
* **MARATHON 2020**: 2020 levels of pure pain. Can you make it all the way?
|
||||||
* **MARATHON 2020**: 2020 levels of pure pain.
|
|
||||||
|
|
||||||
From other games:
|
From other games:
|
||||||
* **MARATHON A1**: Tetris the Grand Master 1.
|
* **MARATHON A1**: Tetris the Grand Master 1.
|
||||||
* **MARATHON A2**: Tetris the Grand Master 2 (TAP Master).
|
* **MARATHON A2**: Tetris the Grand Master 2 (TAP Master).
|
||||||
* **MARATHON A3**: Tetris the Grand Master 3 (no exams).
|
* **MARATHON A3**: Tetris the Grand Master 3 (no exams).
|
||||||
|
* **MARATHON AX4**: Another mode from TGM Ace.
|
||||||
* **MARATHON N1**: NES Tetris A-type.
|
* **MARATHON C89**: Nintendo NES Tetris. Can you transition and make it to the killscreen?
|
||||||
|
|
||||||
|
|
||||||
SURVIVAL
|
SURVIVAL
|
||||||
@@ -25,9 +37,10 @@ SURVIVAL
|
|||||||
|
|
||||||
Modes that concentrate on how long you can survive an increasingly fast and difficult game.
|
Modes that concentrate on how long you can survive an increasingly fast and difficult game.
|
||||||
|
|
||||||
* **SURVIVAL**:
|
* **SURVIVAL 2020**: It only gets worse from Marathon 2020. Beware of the total delay!
|
||||||
|
|
||||||
From other games:
|
From other games:
|
||||||
|
* **SURVIVAL A1**: 20G mode from Tetris the Grand Master.
|
||||||
* **SURVIVAL A2**: T.A. Death.
|
* **SURVIVAL A2**: T.A. Death.
|
||||||
* **SURVIVAL A3**: Ti Shirase.
|
* **SURVIVAL A3**: Ti Shirase.
|
||||||
|
|
||||||
@@ -35,9 +48,29 @@ From other games:
|
|||||||
RACE
|
RACE
|
||||||
----
|
----
|
||||||
|
|
||||||
Modes in which the goal is to achieve a fixed goal in the shortest time.
|
Modes with no levels, just a single timed goal.
|
||||||
|
|
||||||
* **RACE 40L**: Clear 40 lines as fast as possible.
|
* **Race 40**: How fast can you clear 40 lines? No limits, no holds barred.
|
||||||
* **RACE 100L**: Clear 100 lines as fast as possible.
|
|
||||||
* **RACE 5K**: Clear 5,000 lines as fast as possible. Don't worry about topping out!
|
|
||||||
* **RACE 10K**: Clear 10,000 lines as fast as possible. Don't worry about topping out!
|
PHANTOM MANIA
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Modes where pieces turn invisible as soon as you lock them. One of Cambridge's signature features.
|
||||||
|
|
||||||
|
* **Phantom Mania**: The classic invisible mode from Nullpomino. Can you handle 999 levels of "the M roll"?
|
||||||
|
|
||||||
|
* **Phantom Mania 2**: Phantom Mania but way faster! Can you face a mode where even the garbage and the next preview turn invisible?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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?
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
Marathon 2020
|
|
||||||
=============
|
|
||||||
|
|
||||||
To celebrate the coming of the year 2020, I've created a new "extended Tetris the Grand Master" mode where the level counter goes up twice as far as normal, all the way up to 2020.
|
|
||||||
|
|
||||||
|
|
||||||
Gameplay
|
|
||||||
--------
|
|
||||||
|
|
||||||
The goal of this game is to reach the end at level 2020.
|
|
||||||
|
|
||||||
Every piece placed increases the level by 1, and every line cleared also increases the level by 1, with bonuses for large numbers of lines:
|
|
||||||
|
|
||||||
| Lines cleared | Levels advanced |
|
|
||||||
|---------------|-----------------|
|
|
||||||
| 1 | 1 |
|
|
||||||
| 2 | 2 |
|
|
||||||
| 3 | 4 |
|
|
||||||
| 4 | 6 |
|
|
||||||
|
|
||||||
When the current level reaches one less than the level at the bottom of the display (usually a multiple of 100), the level will not advance until a line is cleared.
|
|
||||||
|
|
||||||
|
|
||||||
Levels
|
|
||||||
------
|
|
||||||
|
|
||||||
Each section is 100 levels long, except for the last section, whose levels go from 1900 all the way to 2020.
|
|
||||||
|
|
||||||
However, it is possible to be stopped early on if you do not play fast enough.
|
|
||||||
|
|
||||||
|
|
||||||
### Torikans
|
|
||||||
|
|
||||||
There are certain checkpoints at which your current time will be checked and you will be stopped if your time is over a set objective time.
|
|
||||||
|
|
||||||
| Level | Time limit |
|
|
||||||
|-------|------------|
|
|
||||||
| 500 | 6:00.00 |
|
|
||||||
| 900 | 8:30.00 |
|
|
||||||
| 1000 | 8:45.00 |
|
|
||||||
| 1500 | 11:30.00 |
|
|
||||||
| 1900 | 13:15.00 |
|
|
||||||
|
|
||||||
At levels 500, 1000, and 1500, you will be stopped immediately if your time is not under the objective.
|
|
||||||
|
|
||||||
At levels 900 and 1900, the next section will be capped at 999 or 1999 respectively, and you will get a short credit roll when the section is over.
|
|
||||||
|
|
||||||
|
|
||||||
Speed
|
|
||||||
-----
|
|
||||||
|
|
||||||
Marathon 2020 gets faster in two different, independent ways, the gravity curve and the delay curve.
|
|
||||||
|
|
||||||
The gravity curve is always the same at a particular level, while the delay curve can vary based on your previous section time.
|
|
||||||
|
|
||||||
### Gravity Curve
|
|
||||||
|
|
||||||
The gravity curve is the same as it is in the original TGM and TAP.
|
|
||||||
|
|
||||||
### Delay Curve
|
|
||||||
|
|
||||||
The delay curve is shown as in the following table. Line ARE is always equal to ARE.
|
|
||||||
|
|
||||||
If your time in a particular section from 0 to 70 is smaller than the "cool" requirement at that level, your delay curve will be bumped up an extra level at the end of the section.
|
|
||||||
|
|
||||||
The delay curve always advances at least 1 level past level 500, and if you get a section cool when the level is past 500, it will advance 2 levels instead.
|
|
||||||
|
|
||||||
| Level | ARE | Lock | DAS | Line | Cool |
|
|
||||||
|-------|------|------|------|------|------|
|
|
||||||
|0|27|30|15|40|45.00|
|
|
||||||
|100|24|30|12|25|41.50|
|
|
||||||
|200|21|30|12|25|38.50|
|
|
||||||
|300|18|30|9|20|35.00|
|
|
||||||
|400|16|30|9|15|32.50|
|
|
||||||
|500|14|30|8|12|29.20|
|
|
||||||
|600|12|26|8|12|27.20|
|
|
||||||
|700|10|22|8|8|24.80|
|
|
||||||
|800|8|19|7|8|22.80|
|
|
||||||
|900|6|17|7|6|20.60|
|
|
||||||
|1000|6|15|6|6|19.60|
|
|
||||||
|1100|6|15|6|4|19.40|
|
|
||||||
|1200|6|15|6|4|19.40|
|
|
||||||
|1300|5|15|5|4|18.40|
|
|
||||||
|1400|5|15|5|2|18.20|
|
|
||||||
|1500|4|15|4|2|16.20|
|
|
||||||
|1600|4|13|4|2|16.20|
|
|
||||||
|1700|4|11|4|2|16.20|
|
|
||||||
|1800|4|10|4|2|16.20|
|
|
||||||
|1900|4|9|4|2|16.20|
|
|
||||||
|2000|4|8|3|2|15.20|
|
|
||||||
|
|
||||||
In order to get a section cool, your 0-70 section time must be below the cutoff *and* no more than 2 seconds slower than your previous 0-70 time.
|
|
||||||
|
|
||||||
|
|
||||||
Grading
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Basic grades
|
|
||||||
|
|
||||||
Internally, the grade counter is a number that can range from 0 to 30.
|
|
||||||
|
|
||||||
At the beginning of the game, it starts at 0. To increase it, you must bring an internal grade point counter past a certain threshold.
|
|
||||||
|
|
||||||
The threshold is set at 50 points for the first grade, then 100 more points for the next grade, then 150 points for the grade after that, and so on. You reach the maximum level of 30 at a total of 23,250 points.
|
|
||||||
|
|
||||||
A table of the thresholds in grade points required to reach each level is provided below:
|
|
||||||
|
|
||||||
Grade|Threshold
|
|
||||||
-|-
|
|
||||||
0|0
|
|
||||||
1|50
|
|
||||||
2|150
|
|
||||||
3|300
|
|
||||||
4|500
|
|
||||||
5|750
|
|
||||||
6|1050
|
|
||||||
7|1400
|
|
||||||
8|1800
|
|
||||||
9|2250
|
|
||||||
10|2750
|
|
||||||
11|3300
|
|
||||||
12|3900
|
|
||||||
13|4550
|
|
||||||
14|5250
|
|
||||||
15|6000
|
|
||||||
16|6800
|
|
||||||
17|7650
|
|
||||||
18|8550
|
|
||||||
19|9500
|
|
||||||
20|10500
|
|
||||||
21|11550
|
|
||||||
22|12650
|
|
||||||
23|13800
|
|
||||||
24|15000
|
|
||||||
25|16250
|
|
||||||
26|17550
|
|
||||||
27|18900
|
|
||||||
28|20300
|
|
||||||
29|21750
|
|
||||||
**30**|**23250**
|
|
||||||
|
|
||||||
Points are given according to a different scale, the point level. The point level is calculated by taking your current actual level, and adding (100 * the delay level) to it.
|
|
||||||
|
|
||||||
The points given for clearing certain amounts of lines is given as follows:
|
|
||||||
|
|
||||||
Level| x1 | x2 | x3 | x4
|
|
||||||
-|-|-|-|-
|
|
||||||
0|10|20|30|40
|
|
||||||
100|10|20|30|40
|
|
||||||
200|10|20|30|48
|
|
||||||
300|10|20|30|60
|
|
||||||
400|10|20|36|72
|
|
||||||
500|10|21|42|84
|
|
||||||
600|10|24|48|96
|
|
||||||
700|10|27|54|108
|
|
||||||
800|10|30|60|120
|
|
||||||
900|11|33|66|140
|
|
||||||
1000|12|36|72|160
|
|
||||||
1100|13|39|81|180
|
|
||||||
1200|14|42|90|200
|
|
||||||
1300|15|45|99|220
|
|
||||||
1400|16|48|108|240
|
|
||||||
1500|17|52|117|260
|
|
||||||
1600|18|56|126|280
|
|
||||||
1700|19|60|135|300
|
|
||||||
1800|20|64|144|320
|
|
||||||
1900|21|68|153|340
|
|
||||||
2000|22|72|162|360
|
|
||||||
2100|23|76|171|380
|
|
||||||
2200|24|80|180|400
|
|
||||||
2300|25|84|189|420
|
|
||||||
2400|26|88|198|440
|
|
||||||
2500|27|92|207|460
|
|
||||||
2600|28|96|216|480
|
|
||||||
2700|29|100|225|500
|
|
||||||
2800|30|104|234|520
|
|
||||||
2900|31|108|243|540
|
|
||||||
3000|32|112|252|560
|
|
||||||
3100|33|116|261|580
|
|
||||||
3200|34|120|270|600
|
|
||||||
3300|35|124|279|620
|
|
||||||
3400|36|128|288|640
|
|
||||||
3500|37|132|297|660
|
|
||||||
3600|38|136|306|680
|
|
||||||
3700|39|140|315|700
|
|
||||||
3800|40|144|324|720
|
|
||||||
3900|41|148|333|740
|
|
||||||
|
|
||||||
Past level 1000, a 4-line clear will always give (30 * current grade), regardless of point level.
|
|
||||||
|
|
||||||
The remaining 20 grades come from section cools. Every section cool you get boosts your score by one grade. There are no regrets.
|
|
||||||
|
|
||||||
Points are also taken away with the time it takes to lock down a piece. The delay counter starts at 0, and increases by (current grade + 2) every frame. When the counter reaches or exceeds 240, it resets to 0, and 1 grade point is taken away.
|
|
||||||
|
|
||||||
Grades are based on the maximum grade points achieved. Once a grade has been attained, it cannot be lost even if grade points drop below the threshold for that grade.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Stats
|
|
||||||
-----
|
|
||||||
|
|
||||||
* Fewest number of lines/pieces to reach 2020: 1263 pieces / 505 lines [all Tetrises]
|
|
||||||
|
|
||||||
* Most number of lines/pieces to reach 2020: 1448 pieces / 572 lines [all singles / doubles, full board]
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
Phantom Mania 2
|
|
||||||
===============
|
|
||||||
|
|
||||||
The Phantom Mania mode in Nullpomino is based on T.A. Death (Speed Mania), but where everything is invisible. The obvious sequel to such a mode is Phantom Mania 2, which is based on Shirase (Speed Mania 2), but again where everything is invisible.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Gameplay
|
|
||||||
--------
|
|
||||||
|
|
||||||
The goal of this game is to reach the end at level 1300, and then score as many grades as possible in the ending credit roll.
|
|
||||||
|
|
||||||
Each piece disappears as soon as it is placed down. Only the active piece is visible at any given time.
|
|
||||||
|
|
||||||
Every piece placed increases the level by 1, and every line cleared also increases the level by 1, with bonuses for large numbers of lines:
|
|
||||||
|
|
||||||
| Lines cleared | Levels advanced |
|
|
||||||
|---------------|-----------------|
|
|
||||||
| 1 | 1 |
|
|
||||||
| 2 | 2 |
|
|
||||||
| 3 | 4 |
|
|
||||||
| 4 | 6 |
|
|
||||||
|
|
||||||
When the current level reaches one less than the level at the bottom of the display (usually a multiple of 100), the level will not advance until a line is cleared.
|
|
||||||
|
|
||||||
|
|
||||||
Levels
|
|
||||||
------
|
|
||||||
|
|
||||||
Each section is 100 levels long. At certain levels, the speed may get faster, or the difficulty may increase some other way.
|
|
||||||
|
|
||||||
| Level | What happens |
|
|
||||||
|-------|------------|
|
|
||||||
| 500 | Rows of garbage start to advance on the board. The bottom row is copied, appearing for a brief flash before disappearing again. |
|
|
||||||
| 1000 | The garbage stops, but the hold queue turns invisible. |
|
|
||||||
| 1100 | The first next preview turns invisible. |
|
|
||||||
| 1200 | The second next preview turns invisible. |
|
|
||||||
|
|
||||||
Unlike in Shirase mode, the speed of the game does not get faster past level 1000.
|
|
||||||
|
|
||||||
### Torikans
|
|
||||||
|
|
||||||
There are certain checkpoints at which your current time will be checked and you will be stopped if your time is over a set objective time.
|
|
||||||
|
|
||||||
| Level | Time limit |
|
|
||||||
|-------|------------|
|
|
||||||
| 300 | 2:02.00 |
|
|
||||||
| 500 | 3:03.00 |
|
|
||||||
| 800 | 4:45.00 |
|
|
||||||
| 1000 | 5:38.00 |
|
|
||||||
|
|
||||||
At each torikan, you will get a 54-second invisible roll which is worth grade points.
|
|
||||||
|
|
||||||
Only the 1300 roll is in Big Mode.
|
|
||||||
|
|
||||||
|
|
||||||
Grading
|
|
||||||
-------
|
|
||||||
|
|
||||||
Your grade starts at 1, and goes on from S1 to S9, and then M1 upwards indefinitely.
|
|
||||||
|
|
||||||
Your grade advances each section based on your section time. If you achieve a section COOL, you get 2 grade points. If you instead incur a section REGRET, you get no grade points. The normal section time (in between COOL and REGRET) is worth one grade point.
|
|
||||||
|
|
||||||
| Level | Section COOL | Section REGRET |
|
|
||||||
|-------|--------------|----------------|
|
|
||||||
| 0-4 | 36.00 | 48.00 |
|
|
||||||
| 5-9 | 30.00 | 39.00 |
|
|
||||||
| 10-12 | 27.00 | 35.00 |
|
|
||||||
|
|
||||||
|
|
||||||
Then, the invisible roll is also worth grade points.
|
|
||||||
|
|
||||||
| Lines cleared | Grade points |
|
|
||||||
|---------------|--------------|
|
|
||||||
| 1 | 0.02 |
|
|
||||||
| 2 | 0.06 |
|
|
||||||
| 3 | 0.15 |
|
|
||||||
| 4 | 0.40 |
|
|
||||||
| Clear | 1.00 |
|
|
||||||
|
|
||||||
|
|
||||||
The number of grade points you earn correspond to your grade as follows:
|
|
||||||
|
|
||||||
| Points | Grade |
|
|
||||||
|--------|--------|
|
|
||||||
| 0 | 1 |
|
|
||||||
| 1 | S1 |
|
|
||||||
| 2 | S2 |
|
|
||||||
| 3 | S3 |
|
|
||||||
| 4 | S4 |
|
|
||||||
| 5 | S5 |
|
|
||||||
| 6 | S6 |
|
|
||||||
| 7 | S7 |
|
|
||||||
| 8 | S8 |
|
|
||||||
| 9 | S9 |
|
|
||||||
| 10 | m1 |
|
|
||||||
| 11 | m2 |
|
|
||||||
| 12 | m3 |
|
|
||||||
| 13 | m4 |
|
|
||||||
| 14 | m5 |
|
|
||||||
|
|
||||||
And so on. For *x* > 9 grade points, your grade is m(*x*-9).
|
|
||||||
|
|
||||||
If you achieve a section COOL on each section, your rank will be m17 entering the credit roll.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
Phantom Mania comparison
|
|
||||||
========================
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
| | PM | PM2 |
|
|
||||||
|------------|-----|------|
|
|
||||||
| Max level | 999 | 1300 |
|
|
||||||
| Piece preview | 1 | 3 |
|
|
||||||
| Hold | No | Yes |
|
|
||||||
| Graded roll | No | Yes |
|
|
||||||
|
|
||||||
## Torikans
|
|
||||||
|
|
||||||
| Level | PM | PM2 |
|
|
||||||
|------------|-------|-------|
|
|
||||||
| 300 | 2'28" | 2'02" |
|
|
||||||
| 500 | 3'38" | 3'03" |
|
|
||||||
| 800 | 5'23" | 4'40" |
|
|
||||||
| 1000 | --- | 5'38" |
|
|
||||||
|
|
||||||
## Speed Curve
|
|
||||||
|
|
||||||
| Level | PM | PM2 |
|
|
||||||
|------------|-------|-------|
|
|
||||||
| 0 | 16/30 | 10/18 |
|
|
||||||
| 100 | 12/26 | 10/18 |
|
|
||||||
| 200 | 6/22 | 10/17 |
|
|
||||||
| 300 | 6/18 | 4/15 |
|
|
||||||
| 400 | 5/15 | 4/15 |
|
|
||||||
| 500 | 4/15 | 4/13 |
|
|
||||||
| 600+ | 4/15 | 4/12 |
|
|
||||||
@@ -17,17 +17,9 @@ A rotation system defines the following things:
|
|||||||
* The block offsets of each piece orientation.
|
* The block offsets of each piece orientation.
|
||||||
* The wall or floor kicks that will be attempted for each type of rotation.
|
* The wall or floor kicks that will be attempted for each type of rotation.
|
||||||
|
|
||||||
There are three main classes/families of rotation systems:
|
There are four rotation systems currently supported:
|
||||||
|
|
||||||
* **ARIKA**, commonly known as ARS.
|
* Cambridge
|
||||||
* **ARIKA-CLASSIC**, commonly known as Classic ARS.
|
* Classic ARS
|
||||||
* **ARIKA-TI**, commonly known as Ti-ARS, or "ARS with floorkicks".
|
* Ti-ARS
|
||||||
* **STANDARD**, commonly known as SRS.
|
* SRS
|
||||||
* **STANDARD**, or normal SRS.
|
|
||||||
* **STANDARD-EXP**, known as SRS-X in its original Heboris incarnation.
|
|
||||||
* **STANDARD-WORLD**, known as World Rule in TGM3.
|
|
||||||
* **CLASSIC**, commonly known as ORS or NRS (Nintendo). Also houses some traditional rotation systems.
|
|
||||||
* **CLASSIC-1989**, the no-wallkick system used by NES Tetris.
|
|
||||||
* **CLASSIC-1984**, the Electonika-60 system, where the I piece is one space higher than in CLASSIC-1989.
|
|
||||||
* **CLASSIC-SEGA**, the original Sega rotation system that spawned Arika.
|
|
||||||
* **CLASSIC-TENGEN**, the weird one with orientation problems.
|
|
||||||
27
funcs.lua
27
funcs.lua
@@ -1,4 +1,5 @@
|
|||||||
function copy(t)
|
function copy(t)
|
||||||
|
-- returns deep copy of t (as opposed to the shallow copy you get from var = t)
|
||||||
if type(t) ~= "table" then return t end
|
if type(t) ~= "table" then return t end
|
||||||
local meta = getmetatable(t)
|
local meta = getmetatable(t)
|
||||||
local target = {}
|
local target = {}
|
||||||
@@ -7,7 +8,8 @@ function copy(t)
|
|||||||
return target
|
return target
|
||||||
end
|
end
|
||||||
|
|
||||||
function st(tbl)
|
function strTrueValues(tbl)
|
||||||
|
-- returns a concatenation of all the keys in tbl with value true, separated with spaces
|
||||||
str = ""
|
str = ""
|
||||||
for k, v in pairs(tbl) do
|
for k, v in pairs(tbl) do
|
||||||
if v == true then
|
if v == true then
|
||||||
@@ -17,14 +19,16 @@ function st(tbl)
|
|||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
function sp(m, s, f)
|
function frameTime(min, sec, hth)
|
||||||
if m == nil then m = 0 end
|
-- returns a time in frames from a time in minutes-seconds-hundredths format
|
||||||
if s == nil then s = 0 end
|
if min == nil then min = 0 end
|
||||||
if f == nil then f = 0 end
|
if sec == nil then sec = 0 end
|
||||||
return m*3600 + s*60 + math.ceil(f * 0.6)
|
if hth == nil then hth = 0 end
|
||||||
|
return min*3600 + sec*60 + math.ceil(hth * 0.6)
|
||||||
end
|
end
|
||||||
|
|
||||||
function vAdd(v1, v2)
|
function vAdd(v1, v2)
|
||||||
|
-- returns the sum of vectors v1 and v2
|
||||||
return {
|
return {
|
||||||
x = v1.x + v2.x,
|
x = v1.x + v2.x,
|
||||||
y = v1.y + v2.y
|
y = v1.y + v2.y
|
||||||
@@ -32,6 +36,7 @@ function vAdd(v1, v2)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function vNeg(v)
|
function vNeg(v)
|
||||||
|
-- returns the opposite of vector v
|
||||||
return {
|
return {
|
||||||
x = -v.x,
|
x = -v.x,
|
||||||
y = -v.y
|
y = -v.y
|
||||||
@@ -39,14 +44,18 @@ function vNeg(v)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function formatTime(frames)
|
function formatTime(frames)
|
||||||
|
-- returns a mm:ss:hh (h=hundredths) representation of the time in frames given
|
||||||
if frames < 0 then return formatTime(0) end
|
if frames < 0 then return formatTime(0) end
|
||||||
str = string.format("%02d", math.floor(frames / 3600)) .. ":"
|
local min, sec, hund
|
||||||
.. string.format("%02d", math.floor(frames / 60) % 60) .. "."
|
min = math.floor(frames/3600)
|
||||||
.. string.format("%02d", math.floor(frames / 0.6) % 100)
|
sec = math.floor(frames/60) % 60
|
||||||
|
hund = math.floor(frames/.6) % 100
|
||||||
|
str = string.format("%02d:%02d.%02d", min, sec, hund)
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
function formatBigNum(number)
|
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 s = string.format("%d", number)
|
||||||
local pos = string.len(s) % 3
|
local pos = string.len(s) % 3
|
||||||
if pos == 0 then pos = 3 end
|
if pos == 0 then pos = 3 end
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ backgrounds = {
|
|||||||
love.graphics.newImage("res/backgrounds/1800-railways.png"),
|
love.graphics.newImage("res/backgrounds/1800-railways.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
|
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
|
||||||
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
|
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
|
||||||
|
input_config = love.graphics.newImage("res/backgrounds/options-gears.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks = {
|
blocks = {
|
||||||
@@ -49,6 +50,12 @@ blocks = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for name, blockset in pairs(blocks) do
|
||||||
|
for shape, image in pairs(blockset) do
|
||||||
|
image:setFilter("nearest")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
misc_graphics = {
|
misc_graphics = {
|
||||||
frame = love.graphics.newImage("res/img/frame.png"),
|
frame = love.graphics.newImage("res/img/frame.png"),
|
||||||
ready = love.graphics.newImage("res/img/ready.png"),
|
ready = love.graphics.newImage("res/img/ready.png"),
|
||||||
|
|||||||
27
main.lua
27
main.lua
@@ -11,6 +11,9 @@ function love.load()
|
|||||||
config["side_next"] = false
|
config["side_next"] = false
|
||||||
config["reverse_rotate"] = true
|
config["reverse_rotate"] = true
|
||||||
config["fullscreen"] = false
|
config["fullscreen"] = false
|
||||||
|
|
||||||
|
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
||||||
|
|
||||||
if not config.input then
|
if not config.input then
|
||||||
config.input = {}
|
config.input = {}
|
||||||
scene = InputConfigScene()
|
scene = InputConfigScene()
|
||||||
@@ -61,18 +64,18 @@ end
|
|||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
love.graphics.push()
|
love.graphics.push()
|
||||||
if love.window.getFullscreen() then
|
|
||||||
-- get offset matrix
|
-- get offset matrix
|
||||||
love.graphics.setDefaultFilter("linear", "nearest")
|
love.graphics.setDefaultFilter("linear", "nearest")
|
||||||
local width = love.graphics.getWidth()
|
local width = love.graphics.getWidth()
|
||||||
local height = love.graphics.getHeight()
|
local height = love.graphics.getHeight()
|
||||||
local scale_factor = math.min(width / 640, height / 480)
|
local scale_factor = math.min(width / 640, height / 480)
|
||||||
love.graphics.translate(
|
love.graphics.translate(
|
||||||
(width - scale_factor * 640) / 2,
|
(width - scale_factor * 640) / 2,
|
||||||
(height - scale_factor * 480) / 2
|
(height - scale_factor * 480) / 2
|
||||||
)
|
)
|
||||||
love.graphics.scale(scale_factor)
|
love.graphics.scale(scale_factor)
|
||||||
end
|
|
||||||
scene:render()
|
scene:render()
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
res/backgrounds/options-gears.png
Normal file
BIN
res/backgrounds/options-gears.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
@@ -7,6 +7,7 @@ function Scene:update() end
|
|||||||
function Scene:render() end
|
function Scene:render() end
|
||||||
function Scene:onKeyPress() end
|
function Scene:onKeyPress() end
|
||||||
|
|
||||||
|
ExitScene = require "scene.exit"
|
||||||
GameScene = require "scene.game"
|
GameScene = require "scene.game"
|
||||||
ModeSelectScene = require "scene.mode_select"
|
ModeSelectScene = require "scene.mode_select"
|
||||||
InputConfigScene = require "scene.input_config"
|
InputConfigScene = require "scene.input_config"
|
||||||
|
|||||||
23
scene/exit.lua
Normal file
23
scene/exit.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
local ExitScene = Scene:extend()
|
||||||
|
require 'load.save'
|
||||||
|
|
||||||
|
ExitScene.title = "Exit Game"
|
||||||
|
|
||||||
|
function ExitScene:new()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitScene:update()
|
||||||
|
love.event.quit()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitScene:render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitScene:changeOption(rel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitScene:onKeyPress(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ExitScene
|
||||||
|
|
||||||
@@ -57,11 +57,21 @@ end
|
|||||||
|
|
||||||
function GameScene:onKeyPress(e)
|
function GameScene:onKeyPress(e)
|
||||||
if (self.game.completed) and
|
if (self.game.completed) and
|
||||||
e.scancode == "return" and e.isRepeat == false then
|
(e.scancode == "return" or e.scancode == "escape") and e.isRepeat == false then
|
||||||
highscore_entry = self.game:getHighscoreData()
|
highscore_entry = self.game:getHighscoreData()
|
||||||
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
|
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
|
||||||
submitHighscore(highscore_hash, highscore_entry)
|
submitHighscore(highscore_hash, highscore_entry)
|
||||||
scene = ModeSelectScene()
|
scene = ModeSelectScene()
|
||||||
|
elseif (e.scancode == config.input.retry) then
|
||||||
|
-- fuck this, this is hacky but the way this codebase is setup prevents anything else
|
||||||
|
-- it seems like all the values that get touched in the child gamemode class
|
||||||
|
-- stop being linked to the values of the GameMode superclass because of how `mt.__index` works
|
||||||
|
-- not even sure this is the actual problem, but I don't want to have to rebuild everything about
|
||||||
|
-- the core organisation of everything. this hacky way will have to do until someone figures out something.
|
||||||
|
love.keypressed("escape", "escape", false)
|
||||||
|
love.keypressed("return", "return", false)
|
||||||
|
elseif e.scancode == "escape" then
|
||||||
|
scene = ModeSelectScene()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ local configurable_inputs = {
|
|||||||
"rotate_right2",
|
"rotate_right2",
|
||||||
"rotate_180",
|
"rotate_180",
|
||||||
"hold",
|
"hold",
|
||||||
|
"retry",
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConfigScene:new()
|
function ConfigScene:new()
|
||||||
@@ -27,6 +28,13 @@ function ConfigScene:update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function ConfigScene:render()
|
function ConfigScene: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)
|
love.graphics.setFont(font_3x5_2)
|
||||||
for i, input in pairs(configurable_inputs) do
|
for i, input in pairs(configurable_inputs) do
|
||||||
if config.input[input] then
|
if config.input[input] then
|
||||||
|
|||||||
20
scene/mode_select.lua
Normal file → Executable file
20
scene/mode_select.lua
Normal file → Executable file
@@ -8,27 +8,33 @@ current_ruleset = 1
|
|||||||
game_modes = {
|
game_modes = {
|
||||||
require 'tetris.modes.marathon_2020',
|
require 'tetris.modes.marathon_2020',
|
||||||
require 'tetris.modes.survival_2020',
|
require 'tetris.modes.survival_2020',
|
||||||
require 'tetris.modes.demon_mode',
|
--require 'tetris.modes.strategy',
|
||||||
require 'tetris.modes.strategy',
|
--require 'tetris.modes.interval_training',
|
||||||
require 'tetris.modes.interval_training',
|
--require 'tetris.modes.pacer_test',
|
||||||
require 'tetris.modes.pacer_test',
|
--require 'tetris.modes.demon_mode',
|
||||||
require 'tetris.modes.phantom_mania',
|
require 'tetris.modes.phantom_mania',
|
||||||
require 'tetris.modes.phantom_mania2',
|
require 'tetris.modes.phantom_mania2',
|
||||||
require 'tetris.modes.phantom_mania_n',
|
require 'tetris.modes.phantom_mania_n',
|
||||||
require 'tetris.modes.ligne',
|
require 'tetris.modes.race_40',
|
||||||
require 'tetris.modes.marathon_a1',
|
require 'tetris.modes.marathon_a1',
|
||||||
require 'tetris.modes.marathon_a2',
|
require 'tetris.modes.marathon_a2',
|
||||||
require 'tetris.modes.marathon_a3',
|
require 'tetris.modes.marathon_a3',
|
||||||
|
require 'tetris.modes.marathon_ax4',
|
||||||
|
require 'tetris.modes.marathon_c89',
|
||||||
require 'tetris.modes.survival_a1',
|
require 'tetris.modes.survival_a1',
|
||||||
require 'tetris.modes.survival_a2',
|
require 'tetris.modes.survival_a2',
|
||||||
require 'tetris.modes.survival_a3',
|
require 'tetris.modes.survival_a3',
|
||||||
require 'tetris.modes.marathon_l1',
|
require 'tetris.modes.big_a2',
|
||||||
|
require 'tetris.modes.konoha',
|
||||||
}
|
}
|
||||||
|
|
||||||
rulesets = {
|
rulesets = {
|
||||||
require 'tetris.rulesets.cambridge',
|
require 'tetris.rulesets.cambridge',
|
||||||
require 'tetris.rulesets.arika',
|
require 'tetris.rulesets.arika',
|
||||||
require 'tetris.rulesets.arika_ti',
|
require 'tetris.rulesets.arika_ti',
|
||||||
|
require 'tetris.rulesets.ti_srs',
|
||||||
|
require 'tetris.rulesets.arika_ace',
|
||||||
|
require 'tetris.rulesets.arika_srs',
|
||||||
require 'tetris.rulesets.standard_exp',
|
require 'tetris.rulesets.standard_exp',
|
||||||
--require 'tetris.rulesets.bonkers',
|
--require 'tetris.rulesets.bonkers',
|
||||||
--require 'tetris.rulesets.shirase',
|
--require 'tetris.rulesets.shirase',
|
||||||
@@ -95,6 +101,8 @@ function ModeSelectScene:onKeyPress(e)
|
|||||||
elseif (e.scancode == config.input["left"] or e.scancode == "left") or
|
elseif (e.scancode == config.input["left"] or e.scancode == "left") or
|
||||||
(e.scancode == config.input["right"] or e.scancode == "right") then
|
(e.scancode == config.input["right"] or e.scancode == "right") then
|
||||||
self:switchSelect()
|
self:switchSelect()
|
||||||
|
elseif e.scancode == "escape" then
|
||||||
|
scene = TitleScene()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ local TitleScene = Scene:extend()
|
|||||||
local main_menu_screens = {
|
local main_menu_screens = {
|
||||||
ModeSelectScene,
|
ModeSelectScene,
|
||||||
InputConfigScene,
|
InputConfigScene,
|
||||||
|
ExitScene,
|
||||||
}
|
}
|
||||||
|
|
||||||
function TitleScene:new()
|
function TitleScene:new()
|
||||||
@@ -43,6 +44,8 @@ function TitleScene:onKeyPress(e)
|
|||||||
self:changeOption(-1)
|
self:changeOption(-1)
|
||||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
||||||
self:changeOption(1)
|
self:changeOption(1)
|
||||||
|
elseif e.scancode == "escape" and e.isRepeat == false then
|
||||||
|
love.event.quit()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ local Object = require 'libs.classic'
|
|||||||
local Grid = Object:extend()
|
local Grid = Object:extend()
|
||||||
|
|
||||||
local empty = { skin = "", colour = "" }
|
local empty = { skin = "", colour = "" }
|
||||||
|
local oob = { skin = "", colour = "" }
|
||||||
|
|
||||||
function Grid:new()
|
function Grid:new()
|
||||||
self.grid = {}
|
self.grid = {}
|
||||||
@@ -26,8 +27,15 @@ function Grid:clear()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Grid:getCell(x, y)
|
||||||
|
if x < 1 or x > 10 or y > 24 then return oob
|
||||||
|
elseif y < 1 then return empty
|
||||||
|
else return self.grid[y][x]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Grid:isOccupied(x, y)
|
function Grid:isOccupied(x, y)
|
||||||
return self.grid[y+1][x+1] ~= empty
|
return self:getCell(x+1, y+1) ~= empty
|
||||||
end
|
end
|
||||||
|
|
||||||
function Grid:isRowFull(row)
|
function Grid:isRowFull(row)
|
||||||
@@ -38,11 +46,32 @@ function Grid:isRowFull(row)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Grid:canPlacePiece(piece)
|
function Grid:canPlacePiece(piece)
|
||||||
|
if piece.big then
|
||||||
|
return self:canPlaceBigPiece(piece)
|
||||||
|
end
|
||||||
|
|
||||||
local offsets = piece:getBlockOffsets()
|
local offsets = piece:getBlockOffsets()
|
||||||
for index, offset in pairs(offsets) do
|
for index, offset in pairs(offsets) do
|
||||||
local x = piece.position.x + offset.x
|
local x = piece.position.x + offset.x
|
||||||
local y = piece.position.y + offset.y
|
local y = piece.position.y + offset.y
|
||||||
if x >= 10 or x < 0 or y >= 24 or y < 0 or self.grid[y+1][x+1] ~= empty then
|
if self:isOccupied(x, y) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:canPlaceBigPiece(piece)
|
||||||
|
local offsets = piece:getBlockOffsets()
|
||||||
|
for index, offset in pairs(offsets) do
|
||||||
|
local x = piece.position.x + offset.x
|
||||||
|
local y = piece.position.y + offset.y
|
||||||
|
if (
|
||||||
|
self:isOccupied(x * 2 + 0, y * 2 + 0)
|
||||||
|
or self:isOccupied(x * 2 + 1, y * 2 + 0)
|
||||||
|
or self:isOccupied(x * 2 + 0, y * 2 + 1)
|
||||||
|
or self:isOccupied(x * 2 + 1, y * 2 + 1)
|
||||||
|
) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -50,11 +79,16 @@ function Grid:canPlacePiece(piece)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Grid:canPlacePieceInVisibleGrid(piece)
|
function Grid:canPlacePieceInVisibleGrid(piece)
|
||||||
|
if piece.big then
|
||||||
|
return self:canPlaceBigPiece(piece)
|
||||||
|
-- forget canPlaceBigPieceInVisibleGrid for now
|
||||||
|
end
|
||||||
|
|
||||||
local offsets = piece:getBlockOffsets()
|
local offsets = piece:getBlockOffsets()
|
||||||
for index, offset in pairs(offsets) do
|
for index, offset in pairs(offsets) do
|
||||||
local x = piece.position.x + offset.x
|
local x = piece.position.x + offset.x
|
||||||
local y = piece.position.y + offset.y
|
local y = piece.position.y + offset.y
|
||||||
if x >= 10 or x < 0 or y >= 24 or y < 4 or self.grid[y+1][x+1] ~= empty then
|
if y < 4 or self:isOccupied(x, y) ~= empty then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -116,17 +150,84 @@ function Grid:copyBottomRow()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Grid:applyPiece(piece)
|
function Grid:applyPiece(piece)
|
||||||
|
if piece.big then
|
||||||
|
self:applyBigPiece(piece)
|
||||||
|
return
|
||||||
|
end
|
||||||
offsets = piece:getBlockOffsets()
|
offsets = piece:getBlockOffsets()
|
||||||
for index, offset in pairs(offsets) do
|
for index, offset in pairs(offsets) do
|
||||||
x = piece.position.x + offset.x
|
x = piece.position.x + offset.x
|
||||||
y = piece.position.y + offset.y
|
y = piece.position.y + offset.y
|
||||||
self.grid[y+1][x+1] = {
|
if y + 1 > 0 then
|
||||||
skin = piece.skin,
|
self.grid[y+1][x+1] = {
|
||||||
colour = piece.shape
|
skin = piece.skin,
|
||||||
}
|
colour = piece.shape
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Grid:applyBigPiece(piece)
|
||||||
|
offsets = piece:getBlockOffsets()
|
||||||
|
for index, offset in pairs(offsets) do
|
||||||
|
x = piece.position.x + offset.x
|
||||||
|
y = piece.position.y + offset.y
|
||||||
|
for a = 1, 2 do
|
||||||
|
for b = 1, 2 do
|
||||||
|
if y*2+a > 0 then
|
||||||
|
self.grid[y*2+a][x*2+b] = {
|
||||||
|
skin = piece.skin,
|
||||||
|
colour = piece.shape
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:checkForBravo(cleared_row_count)
|
||||||
|
for i = 0, 23 - cleared_row_count do
|
||||||
|
for j = 0, 9 do
|
||||||
|
if self:isOccupied(j, i) then return false end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:checkSecretGrade()
|
||||||
|
local sgrade = 0
|
||||||
|
for i=23,5,-1 do
|
||||||
|
local validLine = true
|
||||||
|
local emptyCell = 0
|
||||||
|
if i > 13 then
|
||||||
|
emptyCell = 23-i
|
||||||
|
end
|
||||||
|
if i <= 13 then
|
||||||
|
emptyCell = i-5
|
||||||
|
end
|
||||||
|
for j=0,9 do
|
||||||
|
if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then
|
||||||
|
validLine = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not self:isOccupied(emptyCell,i-1) then
|
||||||
|
validLine = false
|
||||||
|
end
|
||||||
|
if(validLine) then
|
||||||
|
sgrade = sgrade + 1
|
||||||
|
else
|
||||||
|
-- return sgrade
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--[[
|
||||||
|
if(sgrade == 0) then return ""
|
||||||
|
elseif(sgrade < 10) then return 10-sgrade
|
||||||
|
elseif(sgrade < 19) then return "S"..(sgrade-9) end
|
||||||
|
return "GM"
|
||||||
|
--]]
|
||||||
|
return sgrade
|
||||||
|
end
|
||||||
|
|
||||||
function Grid:update()
|
function Grid:update()
|
||||||
for y = 1, 24 do
|
for y = 1, 24 do
|
||||||
for x = 1, 10 do
|
for x = 1, 10 do
|
||||||
@@ -148,19 +249,21 @@ function Grid:draw()
|
|||||||
love.graphics.setColor(0.5, 0.5, 0.5, 1)
|
love.graphics.setColor(0.5, 0.5, 0.5, 1)
|
||||||
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
||||||
end
|
end
|
||||||
love.graphics.setColor(0.8, 0.8, 0.8, 1)
|
if self.grid[y][x].skin ~= "bone" then
|
||||||
love.graphics.setLineWidth(1)
|
love.graphics.setColor(0.8, 0.8, 0.8, 1)
|
||||||
if y > 1 and self.grid[y-1][x] == empty then
|
love.graphics.setLineWidth(1)
|
||||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
if y > 1 and self.grid[y-1][x] == empty then
|
||||||
end
|
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||||
if y < 24 and self.grid[y+1][x] == empty then
|
end
|
||||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
if y < 24 and self.grid[y+1][x] == empty then
|
||||||
end
|
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||||
if x > 1 and self.grid[y][x-1] == empty then
|
end
|
||||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
if x > 1 and self.grid[y][x-1] == empty then
|
||||||
end
|
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||||
if x < 10 and self.grid[y][x+1] == empty then
|
end
|
||||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local Object = require 'libs.classic'
|
|||||||
|
|
||||||
local Piece = Object:extend()
|
local Piece = Object:extend()
|
||||||
|
|
||||||
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin)
|
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, big)
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
self.position = position
|
self.position = position
|
||||||
@@ -12,6 +12,7 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay
|
|||||||
self.skin = skin
|
self.skin = skin
|
||||||
self.ghost = false
|
self.ghost = false
|
||||||
self.locked = false
|
self.locked = false
|
||||||
|
self.big = big
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Functions that return a new piece to test in rotation systems.
|
-- Functions that return a new piece to test in rotation systems.
|
||||||
@@ -20,7 +21,7 @@ function Piece:withOffset(offset)
|
|||||||
return Piece(
|
return Piece(
|
||||||
self.shape, self.rotation,
|
self.shape, self.rotation,
|
||||||
{x = self.position.x + offset.x, y = self.position.y + offset.y},
|
{x = self.position.x + offset.x, y = self.position.y + offset.y},
|
||||||
self.block_offsets, self.gravity, self.lock_delay, self.skin
|
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ function Piece:withRelativeRotation(rot)
|
|||||||
while new_rot >= 4 do new_rot = new_rot - 4 end
|
while new_rot >= 4 do new_rot = new_rot - 4 end
|
||||||
return Piece(
|
return Piece(
|
||||||
self.shape, new_rot, self.position,
|
self.shape, new_rot, self.position,
|
||||||
self.block_offsets, self.gravity, self.lock_delay, self.skin
|
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ end
|
|||||||
|
|
||||||
function Piece:dropToBottom(grid)
|
function Piece:dropToBottom(grid)
|
||||||
local piece_y = self.position.y
|
local piece_y = self.position.y
|
||||||
self:dropSquares(20, grid)
|
self:dropSquares(24, grid)
|
||||||
self.gravity = 0
|
self.gravity = 0
|
||||||
if self.position.y > piece_y then
|
if self.position.y > piece_y then
|
||||||
-- if it got dropped any, also reset lock delay
|
-- if it got dropped any, also reset lock delay
|
||||||
@@ -138,14 +139,25 @@ function Piece:draw(opacity, brightness, grid, partial_das)
|
|||||||
love.graphics.setColor(brightness, brightness, brightness, opacity)
|
love.graphics.setColor(brightness, brightness, brightness, opacity)
|
||||||
local offsets = self:getBlockOffsets()
|
local offsets = self:getBlockOffsets()
|
||||||
local gravity_offset = 0
|
local gravity_offset = 0
|
||||||
if grid ~= nil and not self:isDropBlocked(grid) then
|
--if grid ~= nil and not self:isDropBlocked(grid) then
|
||||||
gravity_offset = self.gravity * 16
|
-- gravity_offset = self.gravity * 16
|
||||||
end
|
--end
|
||||||
if partial_das == nil then partial_das = 0 end
|
if partial_das == nil then partial_das = 0 end
|
||||||
for index, offset in pairs(offsets) do
|
for index, offset in pairs(offsets) do
|
||||||
local x = self.position.x + offset.x
|
local x = self.position.x + offset.x
|
||||||
local y = self.position.y + offset.y
|
local y = self.position.y + offset.y
|
||||||
love.graphics.draw(blocks[self.skin][self.shape], 64+x*16+partial_das, 16+y*16+gravity_offset)
|
if self.big then
|
||||||
|
love.graphics.draw(
|
||||||
|
blocks[self.skin][self.shape],
|
||||||
|
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
|
||||||
|
0, 2, 2
|
||||||
|
)
|
||||||
|
else
|
||||||
|
love.graphics.draw(
|
||||||
|
blocks[self.skin][self.shape],
|
||||||
|
64+x*16+partial_das, 16+y*16+gravity_offset
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
365
tetris/modes/big_a2.lua
Executable file
365
tetris/modes/big_a2.lua
Executable file
@@ -0,0 +1,365 @@
|
|||||||
|
require 'funcs'
|
||||||
|
|
||||||
|
local GameMode = require 'tetris.modes.gamemode'
|
||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
|
||||||
|
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||||
|
|
||||||
|
local MarathonA2Game = GameMode:extend()
|
||||||
|
|
||||||
|
MarathonA2Game.name = "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()
|
||||||
|
self.big_mode = true
|
||||||
|
self.roll_frames = 0
|
||||||
|
self.combo = 1
|
||||||
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
self.grade = 0
|
||||||
|
self.grade_points = 0
|
||||||
|
self.grade_point_decay_counter = 0
|
||||||
|
self.section_start_time = 0
|
||||||
|
self.section_times = { [0] = 0 }
|
||||||
|
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||||
|
|
||||||
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
|
self.lock_drop = false
|
||||||
|
self.enable_hold = false
|
||||||
|
self.next_queue_length = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getARE()
|
||||||
|
if self.level < 700 then return 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 > 3694 then
|
||||||
|
self.completed = true
|
||||||
|
if self.grade == 32 then
|
||||||
|
self.grade = 33
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif self.ready_frames == 0 then
|
||||||
|
self.frames = self.frames + 1
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:onPieceEnter()
|
||||||
|
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||||
|
self.level = self.level + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||||
|
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
|
||||||
|
if self:qualifiesForMRoll() then
|
||||||
|
self.grade = 32
|
||||||
|
end
|
||||||
|
self.grid:clear()
|
||||||
|
self.roll_frames = -150
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
cleared_lines = cleared_lines / 2
|
||||||
|
self:updateGrade(cleared_lines)
|
||||||
|
if cleared_lines > 0 then
|
||||||
|
self.score = self.score + (
|
||||||
|
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||||
|
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||||
|
)
|
||||||
|
self.lines = self.lines + cleared_lines
|
||||||
|
self.combo = self.combo + cleared_lines - 1
|
||||||
|
else
|
||||||
|
self.drop_bonus = 0
|
||||||
|
self.combo = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
||||||
|
if self.clear then return end
|
||||||
|
if math.floor(old_level / 100) < math.floor(new_level / 100) or
|
||||||
|
new_level >= 999 then
|
||||||
|
-- record new section
|
||||||
|
section_time = self.frames - self.section_start_time
|
||||||
|
self.section_times[math.floor(old_level / 100)] = section_time
|
||||||
|
self.section_start_time = self.frames
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local grade_point_bonuses = {
|
||||||
|
{10, 20, 40, 50},
|
||||||
|
{10, 20, 30, 40},
|
||||||
|
{10, 20, 30, 40},
|
||||||
|
{10, 15, 30, 40},
|
||||||
|
{10, 15, 20, 40},
|
||||||
|
{5, 15, 20, 30},
|
||||||
|
{5, 10, 20, 30},
|
||||||
|
{5, 10, 15, 30},
|
||||||
|
{5, 10, 15, 30},
|
||||||
|
{5, 10, 15, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
{2, 12, 13, 30},
|
||||||
|
}
|
||||||
|
|
||||||
|
local grade_point_decays = {
|
||||||
|
125, 80, 80, 50, 45, 45, 45,
|
||||||
|
40, 40, 40, 40, 40, 30, 30, 30,
|
||||||
|
20, 20, 20, 20, 20,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
10, 10
|
||||||
|
}
|
||||||
|
|
||||||
|
local combo_multipliers = {
|
||||||
|
{1.0, 1.0, 1.0, 1.0},
|
||||||
|
{1.2, 1.4, 1.5, 1.0},
|
||||||
|
{1.2, 1.5, 1.8, 1.0},
|
||||||
|
{1.4, 1.6, 2.0, 1.0},
|
||||||
|
{1.4, 1.7, 2.2, 1.0},
|
||||||
|
{1.4, 1.8, 2.3, 1.0},
|
||||||
|
{1.4, 1.9, 2.4, 1.0},
|
||||||
|
{1.5, 2.0, 2.5, 1.0},
|
||||||
|
{1.5, 2.1, 2.6, 1.0},
|
||||||
|
{2.0, 2.5, 3.0, 1.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
local grade_conversion = {
|
||||||
|
[0] = 0,
|
||||||
|
1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
|
||||||
|
7, 8, 8, 8, 9, 9, 9, 10, 11, 12,
|
||||||
|
12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||||
|
17, 18, 19
|
||||||
|
}
|
||||||
|
|
||||||
|
function MarathonA2Game:updateGrade(cleared_lines)
|
||||||
|
if self.clear then return end
|
||||||
|
if cleared_lines == 0 then
|
||||||
|
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||||
|
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||||
|
self.grade_point_decay_counter = 0
|
||||||
|
self.grade_points = math.max(0, self.grade_points - 1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.grade_points = self.grade_points + (
|
||||||
|
math.ceil(
|
||||||
|
grade_point_bonuses[self.grade + 1][cleared_lines] *
|
||||||
|
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
|
||||||
|
) * (1 + math.floor(self.level / 250))
|
||||||
|
)
|
||||||
|
if self.grade_points >= 100 and self.grade < 31 then
|
||||||
|
self.grade_points = 0
|
||||||
|
self.grade = self.grade + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
|
||||||
|
|
||||||
|
function MarathonA2Game:qualifiesForMRoll()
|
||||||
|
if not self.clear then return false end
|
||||||
|
-- tetris requirements
|
||||||
|
for section = 0, 9 do
|
||||||
|
if self.section_tetrises[section] < tetris_requirements[section] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- section time requirements
|
||||||
|
local section_average = 0
|
||||||
|
for section = 0, 4 do
|
||||||
|
section_average = section_average + self.section_times[section]
|
||||||
|
if self.section_times[section] > frameTime(1,05) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- section time average requirements
|
||||||
|
if self.section_times[5] > section_average / 5 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for section = 6, 9 do
|
||||||
|
if self.section_times[section] > self.section_times[section - 1] + 120 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.grade < 17 or self.frames > frameTime(8,45) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getLetterGrade()
|
||||||
|
local grade = grade_conversion[self.grade]
|
||||||
|
if grade < 9 then
|
||||||
|
return tostring(9 - grade)
|
||||||
|
elseif grade < 18 then
|
||||||
|
return "S" .. tostring(grade - 8)
|
||||||
|
elseif grade == 18 then
|
||||||
|
return "M"
|
||||||
|
else
|
||||||
|
return "GM"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MarathonA2Game.rollOpacityFunction = function(age)
|
||||||
|
if age < 240 then return 1
|
||||||
|
elseif age > 300 then return 0
|
||||||
|
else return 1 - (age - 240) / 60 end
|
||||||
|
end
|
||||||
|
|
||||||
|
MarathonA2Game.mRollOpacityFunction = function(age)
|
||||||
|
if age > 4 then return 0
|
||||||
|
else return 1 - age / 4 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:drawGrid(ruleset)
|
||||||
|
if self.clear and not (self.completed or self.game_over) then
|
||||||
|
if self:qualifiesForMRoll() then
|
||||||
|
self.grid:drawInvisible(self.mRollOpacityFunction)
|
||||||
|
else
|
||||||
|
self.grid:drawInvisible(self.rollOpacityFunction)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:drawScoringInfo()
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
love.graphics.print(
|
||||||
|
self.das.direction .. " " ..
|
||||||
|
self.das.frames .. " " ..
|
||||||
|
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)
|
||||||
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_8x11)
|
||||||
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getHighscoreData()
|
||||||
|
return {
|
||||||
|
grade = grade_conversion[self.grade],
|
||||||
|
score = self.score,
|
||||||
|
level = self.level,
|
||||||
|
frames = self.frames,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getSectionEndLevel()
|
||||||
|
if self.level >= 900 then return 999
|
||||||
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getBackground()
|
||||||
|
return math.floor(self.level / 100)
|
||||||
|
end
|
||||||
|
|
||||||
|
return MarathonA2Game
|
||||||
@@ -11,8 +11,8 @@ DemonModeGame.name = "Demon Mode"
|
|||||||
DemonModeGame.hash = "DemonMode"
|
DemonModeGame.hash = "DemonMode"
|
||||||
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
|
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
|
||||||
|
|
||||||
DemonModeGame.arr = 1
|
|
||||||
DemonModeGame.drop_speed = 1
|
|
||||||
|
|
||||||
function DemonModeGame:new()
|
function DemonModeGame:new()
|
||||||
DemonModeGame.super:new()
|
DemonModeGame.super:new()
|
||||||
@@ -104,7 +104,7 @@ function DemonModeGame:advanceOneFrame()
|
|||||||
if self.clear then
|
if self.clear then
|
||||||
self.roll_frames = self.roll_frames + 1
|
self.roll_frames = self.roll_frames + 1
|
||||||
if self.roll_frames < 0 then
|
if self.roll_frames < 0 then
|
||||||
return
|
return false
|
||||||
elseif self.roll_frames >= 1337 then
|
elseif self.roll_frames >= 1337 then
|
||||||
self.completed = true
|
self.completed = true
|
||||||
end
|
end
|
||||||
@@ -159,7 +159,7 @@ function DemonModeGame:updateSectionTimes(old_level, new_level)
|
|||||||
end
|
end
|
||||||
elseif old_level < 100 then
|
elseif old_level < 100 then
|
||||||
-- If section time is under cutoff, skip to level 500.
|
-- If section time is under cutoff, skip to level 500.
|
||||||
if self.frames < sp(1,00) then
|
if self.frames < frameTime(1,00) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
self.grade = 5
|
self.grade = 5
|
||||||
self.section_tries = 0
|
self.section_tries = 0
|
||||||
@@ -226,7 +226,7 @@ function DemonModeGame:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ function GameMode:new()
|
|||||||
self.enable_hold = false
|
self.enable_hold = false
|
||||||
self.enable_hard_drop = true
|
self.enable_hard_drop = true
|
||||||
self.next_queue_length = 1
|
self.next_queue_length = 1
|
||||||
|
self.additive_gravity = true
|
||||||
self.draw_section_times = false
|
self.draw_section_times = false
|
||||||
self.draw_secondary_section_times = false
|
self.draw_secondary_section_times = false
|
||||||
|
self.big_mode = false
|
||||||
-- variables related to configurable parameters
|
-- variables related to configurable parameters
|
||||||
self.drop_locked = false
|
self.drop_locked = false
|
||||||
self.hard_drop_locked = false
|
self.hard_drop_locked = false
|
||||||
@@ -48,13 +50,6 @@ function GameMode:new()
|
|||||||
self.secondary_section_times = { [0] = 0 }
|
self.secondary_section_times = { [0] = 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:initialize()
|
|
||||||
-- after all the variables are initialized, run initialization procedures
|
|
||||||
for i = 1, 30 do
|
|
||||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function GameMode:getARR() return 1 end
|
function GameMode:getARR() return 1 end
|
||||||
function GameMode:getDropSpeed() return 1 end
|
function GameMode:getDropSpeed() return 1 end
|
||||||
function GameMode:getARE() return 25 end
|
function GameMode:getARE() return 25 end
|
||||||
@@ -73,6 +68,7 @@ end
|
|||||||
|
|
||||||
function GameMode:initialize(ruleset)
|
function GameMode:initialize(ruleset)
|
||||||
-- generate next queue
|
-- generate next queue
|
||||||
|
self:new()
|
||||||
for i = 1, self.next_queue_length do
|
for i = 1, self.next_queue_length do
|
||||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||||
end
|
end
|
||||||
@@ -114,42 +110,50 @@ function GameMode:update(inputs, ruleset)
|
|||||||
self.hard_drop_locked = false
|
self.hard_drop_locked = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local piece_y = self.piece.position.y
|
||||||
|
|
||||||
ruleset:processPiece(
|
ruleset:processPiece(
|
||||||
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
|
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
|
||||||
self.move, self:getLockDelay(), self:getDropSpeed(),
|
self.move, self:getLockDelay(), self:getDropSpeed(),
|
||||||
self.drop_locked, self.hard_drop_locked, self.enable_hard_drop
|
self.drop_locked, self.hard_drop_locked,
|
||||||
|
self.enable_hard_drop, self.additive_gravity
|
||||||
)
|
)
|
||||||
|
|
||||||
|
local piece_dy = self.piece.position.y - piece_y
|
||||||
|
|
||||||
if inputs["up"] == true and
|
if inputs["up"] == true and
|
||||||
self.piece:isDropBlocked(self.grid) and
|
self.piece:isDropBlocked(self.grid) and
|
||||||
not self.hard_drop_locked and
|
not self.hard_drop_locked then
|
||||||
self.instant_hard_drop
|
self:onHardDrop(piece_dy)
|
||||||
then
|
if self.instant_hard_drop then
|
||||||
self.piece.locked = true
|
self.piece.locked = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if inputs["down"] == true and
|
if inputs["down"] == true then
|
||||||
self.piece:isDropBlocked(self.grid) and
|
self:onSoftDrop(piece_dy)
|
||||||
not self.drop_locked and
|
if self.piece:isDropBlocked(self.grid) and
|
||||||
self.instant_soft_drop
|
not self.drop_locked and
|
||||||
then
|
self.instant_soft_drop
|
||||||
self.piece.locked = true
|
then
|
||||||
|
self.piece.locked = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.piece.locked == true then
|
if self.piece.locked == true then
|
||||||
self.grid:applyPiece(self.piece)
|
self.grid:applyPiece(self.piece)
|
||||||
self:onPieceLock(self.piece)
|
|
||||||
self.piece = nil
|
|
||||||
if self.enable_hold then
|
|
||||||
self.held = false
|
|
||||||
end
|
|
||||||
|
|
||||||
self.grid:markClearedRows()
|
self.grid:markClearedRows()
|
||||||
|
|
||||||
local cleared_row_count = self.grid:getClearedRowCount()
|
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:updateScore(self.level, self.drop_bonus, cleared_row_count)
|
||||||
|
|
||||||
|
self.piece = nil
|
||||||
|
if self.enable_hold then
|
||||||
|
self.held = false
|
||||||
|
end
|
||||||
|
|
||||||
if cleared_row_count > 0 then
|
if cleared_row_count > 0 then
|
||||||
self.lcd = self:getLineClearDelay()
|
self.lcd = self:getLineClearDelay()
|
||||||
self.are = self:getLineARE()
|
self.are = self:getLineARE()
|
||||||
@@ -184,11 +188,19 @@ end
|
|||||||
|
|
||||||
-- event functions
|
-- event functions
|
||||||
function GameMode:whilePieceActive() end
|
function GameMode:whilePieceActive() end
|
||||||
function GameMode:onPieceLock(piece) end
|
function GameMode:onPieceLock(piece, cleared_row_count) end
|
||||||
function GameMode:onLineClear(cleared_row_count) end
|
function GameMode:onLineClear(cleared_row_count) end
|
||||||
function GameMode:onPieceEnter() end
|
function GameMode:onPieceEnter() end
|
||||||
function GameMode:onHold() end
|
function GameMode:onHold() end
|
||||||
|
|
||||||
|
function GameMode:onSoftDrop(dropped_row_count)
|
||||||
|
self.drop_bonus = self.drop_bonus + 1 * dropped_row_count
|
||||||
|
end
|
||||||
|
|
||||||
|
function GameMode:onHardDrop(dropped_row_count)
|
||||||
|
self.drop_bonus = self.drop_bonus + 2 * dropped_row_count
|
||||||
|
end
|
||||||
|
|
||||||
function GameMode:onGameOver()
|
function GameMode:onGameOver()
|
||||||
switchBGM(nil)
|
switchBGM(nil)
|
||||||
end
|
end
|
||||||
@@ -283,7 +295,7 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
|
|||||||
inputs, piece_data, self.grid, gravity,
|
inputs, piece_data, self.grid, gravity,
|
||||||
self.prev_inputs, self.move,
|
self.prev_inputs, self.move,
|
||||||
self:getLockDelay(), self:getDropSpeed(),
|
self:getLockDelay(), self:getDropSpeed(),
|
||||||
self.lock_drop, self.lock_hard_drop
|
self.lock_drop, self.lock_hard_drop, self.big_mode
|
||||||
)
|
)
|
||||||
if self.lock_drop then
|
if self.lock_drop then
|
||||||
self.drop_locked = true
|
self.drop_locked = true
|
||||||
@@ -330,8 +342,8 @@ end
|
|||||||
function GameMode:drawNextQueue(ruleset)
|
function GameMode:drawNextQueue(ruleset)
|
||||||
function drawPiece(piece, skin, offsets, pos_x, pos_y)
|
function drawPiece(piece, skin, offsets, pos_x, pos_y)
|
||||||
for index, offset in pairs(offsets) do
|
for index, offset in pairs(offsets) do
|
||||||
local x = ruleset.spawn_positions[piece].x + offset.x
|
local x = offset.x + ruleset.spawn_positions[piece].x
|
||||||
local y = ruleset.spawn_positions[piece].y + offset.y
|
local y = offset.y + 4.7
|
||||||
love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16)
|
love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -374,7 +386,8 @@ function GameMode:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs) ..
|
||||||
|
self.drop_bonus
|
||||||
)
|
)
|
||||||
|
|
||||||
love.graphics.setFont(font_8x11)
|
love.graphics.setFont(font_8x11)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ IntervalTrainingGame.name = "Interval Training"
|
|||||||
IntervalTrainingGame.hash = "IntervalTraining"
|
IntervalTrainingGame.hash = "IntervalTraining"
|
||||||
IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||||
|
|
||||||
IntervalTrainingGame.arr = 1
|
|
||||||
IntervalTrainingGame.drop_speed = 1
|
|
||||||
|
|
||||||
function IntervalTrainingGame:new()
|
function IntervalTrainingGame:new()
|
||||||
IntervalTrainingGame.super:new()
|
IntervalTrainingGame.super:new()
|
||||||
@@ -120,7 +120,7 @@ function IntervalTrainingGame:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||||
@@ -134,7 +134,7 @@ function IntervalTrainingGame:drawScoringInfo()
|
|||||||
|
|
||||||
-- draw time left, flash red if necessary
|
-- draw time left, flash red if necessary
|
||||||
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
||||||
if not self.game_over and not self.clear and time_left < sp(0,10) and time_left % 4 < 2 then
|
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
|
||||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||||
end
|
end
|
||||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||||
@@ -144,7 +144,7 @@ function IntervalTrainingGame:drawScoringInfo()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function IntervalTrainingGame:getSectionEndLevel()
|
function IntervalTrainingGame:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
190
tetris/modes/konoha.lua
Executable file
190
tetris/modes/konoha.lua
Executable file
@@ -0,0 +1,190 @@
|
|||||||
|
require 'funcs'
|
||||||
|
|
||||||
|
local GameMode = require 'tetris.modes.gamemode'
|
||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
|
||||||
|
local KonohaRandomizer = require 'tetris.randomizers.bag_konoha'
|
||||||
|
|
||||||
|
local KonohaGame = GameMode:extend()
|
||||||
|
|
||||||
|
KonohaGame.name = "All Clear A4"
|
||||||
|
KonohaGame.hash = "AllClearA4"
|
||||||
|
KonohaGame.tagline = "Get as many bravos as you can under the time limit!"
|
||||||
|
|
||||||
|
function KonohaGame:new()
|
||||||
|
KonohaGame.super:new()
|
||||||
|
|
||||||
|
self.randomizer = KonohaRandomizer()
|
||||||
|
self.bravos = 0
|
||||||
|
self.last_bonus_amount = 0
|
||||||
|
self.last_bonus_display_time = 0
|
||||||
|
self.time_limit = 10800
|
||||||
|
self.big_mode = true
|
||||||
|
|
||||||
|
self.enable_hold = true
|
||||||
|
self.next_queue_length = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getARE()
|
||||||
|
if self.level < 300 then return 30
|
||||||
|
elseif self.level < 400 then return 25
|
||||||
|
elseif self.level < 500 then return 20
|
||||||
|
elseif self.level < 600 then return 17
|
||||||
|
elseif self.level < 800 then return 15
|
||||||
|
elseif self.level < 900 then return 13
|
||||||
|
elseif self.level < 1000 then return 10
|
||||||
|
elseif self.level < 1300 then return 8
|
||||||
|
else return 6 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getLineARE()
|
||||||
|
return self:getARE()
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getDasLimit()
|
||||||
|
if self.level < 500 then return 10
|
||||||
|
elseif self.level < 800 then return 9
|
||||||
|
elseif self.level < 1000 then return 8
|
||||||
|
else return 7 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getLineClearDelay()
|
||||||
|
if self.level < 200 then return 14
|
||||||
|
elseif self.level < 500 then return 9
|
||||||
|
elseif self.level < 800 then return 8
|
||||||
|
elseif self.level < 1000 then return 7
|
||||||
|
else return 6 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getLockDelay()
|
||||||
|
if self.level < 500 then return 30
|
||||||
|
elseif self.level < 600 then return 25
|
||||||
|
elseif self.level < 700 then return 23
|
||||||
|
elseif self.level < 800 then return 20
|
||||||
|
elseif self.level < 900 then return 17
|
||||||
|
elseif self.level < 1000 then return 15
|
||||||
|
elseif self.level < 1200 then return 13
|
||||||
|
elseif self.level < 1300 then return 10
|
||||||
|
else return 8 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getGravity()
|
||||||
|
if (self.level < 30) then return 4/256
|
||||||
|
elseif (self.level < 35) then return 8/256
|
||||||
|
elseif (self.level < 40) then return 12/256
|
||||||
|
elseif (self.level < 50) then return 16/256
|
||||||
|
elseif (self.level < 60) then return 32/256
|
||||||
|
elseif (self.level < 70) then return 48/256
|
||||||
|
elseif (self.level < 80) then return 64/256
|
||||||
|
elseif (self.level < 90) then return 128/256
|
||||||
|
elseif (self.level < 100) then return 192/256
|
||||||
|
elseif (self.level < 120) then return 1
|
||||||
|
elseif (self.level < 140) then return 2
|
||||||
|
elseif (self.level < 160) then return 3
|
||||||
|
elseif (self.level < 170) then return 4
|
||||||
|
elseif (self.level < 200) then return 5
|
||||||
|
else return 20 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getSection()
|
||||||
|
return math.floor(level / 100) + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getSectionEndLevel()
|
||||||
|
return math.floor(self.level / 100 + 1) * 100
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:advanceOneFrame()
|
||||||
|
if self.ready_frames == 0 then
|
||||||
|
self.time_limit = self.time_limit - 1
|
||||||
|
self.frames = self.frames + 1
|
||||||
|
end
|
||||||
|
if self.time_limit <= 0 then
|
||||||
|
self.game_over = true
|
||||||
|
end
|
||||||
|
self.last_bonus_display_time = self.last_bonus_display_time - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:onPieceEnter()
|
||||||
|
if (self.level % 100 ~= 99) and self.frames ~= 0 then
|
||||||
|
self.level = self.level + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:drawGrid(ruleset)
|
||||||
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cleared_row_levels = {2, 4, 6, 12}
|
||||||
|
local bravo_bonus = {300, 480, 660, 900}
|
||||||
|
local non_bravo_bonus = {0, 0, 20, 40}
|
||||||
|
local bravo_ot_bonus = {0, 60, 120, 180}
|
||||||
|
|
||||||
|
function KonohaGame:onLineClear(cleared_row_count)
|
||||||
|
local oldtime = self.time_limit
|
||||||
|
|
||||||
|
self.level = self.level + cleared_row_levels[cleared_row_count / 2]
|
||||||
|
if self.grid:checkForBravo(cleared_row_count) then
|
||||||
|
self.bravos = self.bravos + 1
|
||||||
|
if self.level < 1000 then self.time_limit = self.time_limit + bravo_bonus[cleared_row_count / 2]
|
||||||
|
else self.time_limit = self.time_limit + bravo_ot_bonus[cleared_row_count / 2]
|
||||||
|
end
|
||||||
|
if self.bravos == 11 then self.randomizer.allowrepeat = true end
|
||||||
|
elseif self.level < 1000 then
|
||||||
|
self.time_limit = self.time_limit + non_bravo_bonus[cleared_row_count / 2]
|
||||||
|
end
|
||||||
|
|
||||||
|
local bonus = self.time_limit - oldtime
|
||||||
|
if bonus > 0 then
|
||||||
|
self.last_bonus_amount = bonus
|
||||||
|
self.last_bonus_display_time = 120
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getBackground()
|
||||||
|
return math.floor(self.level / 100)
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:drawScoringInfo()
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
love.graphics.print(
|
||||||
|
self.das.direction .. " " ..
|
||||||
|
self.das.frames .. " " ..
|
||||||
|
strTrueValues(self.prev_inputs)
|
||||||
|
)
|
||||||
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
|
love.graphics.printf("TIME LIMIT", 240, 120, 120, "left")
|
||||||
|
love.graphics.printf("BRAVOS", 240, 200, 50, "left")
|
||||||
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_3)
|
||||||
|
if not self.game_over and self.time_limit < frameTime(0,10) and self.time_limit % 4 < 2 then
|
||||||
|
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||||
|
end
|
||||||
|
love.graphics.printf(formatTime(self.time_limit), 240, 140, 120, "right")
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
if self.last_bonus_display_time > 0 then
|
||||||
|
love.graphics.printf("+"..formatTime(self.last_bonus_amount), 240, 160, 120, "right")
|
||||||
|
end
|
||||||
|
love.graphics.printf(self.bravos, 240, 220, 90, "left")
|
||||||
|
love.graphics.printf(self.level, 240, 340, 50, "right")
|
||||||
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 50, "right")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_8x11)
|
||||||
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
|
end
|
||||||
|
|
||||||
|
function KonohaGame:getHighscoreData()
|
||||||
|
return {
|
||||||
|
bravos = self.bravos,
|
||||||
|
level = self.level,
|
||||||
|
frames = self.frames,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return KonohaGame
|
||||||
@@ -151,11 +151,11 @@ function Marathon2020Game:advanceOneFrame()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local cool_cutoffs = {
|
local cool_cutoffs = {
|
||||||
sp(0,45,00), sp(0,41,50), sp(0,38,50), sp(0,35,00), sp(0,32,50),
|
frameTime(0,45,00), frameTime(0,41,50), frameTime(0,38,50), frameTime(0,35,00), frameTime(0,32,50),
|
||||||
sp(0,29,20), sp(0,27,20), sp(0,24,80), sp(0,22,80), sp(0,20,60),
|
frameTime(0,29,20), frameTime(0,27,20), frameTime(0,24,80), frameTime(0,22,80), frameTime(0,20,60),
|
||||||
sp(0,19,60), sp(0,19,40), sp(0,19,40), sp(0,18,40), sp(0,18,20),
|
frameTime(0,19,60), frameTime(0,19,40), frameTime(0,19,40), frameTime(0,18,40), frameTime(0,18,20),
|
||||||
sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20),
|
frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20),
|
||||||
sp(0,15,20)
|
frameTime(0,15,20)
|
||||||
}
|
}
|
||||||
|
|
||||||
local levels_for_cleared_rows = { 1, 2, 4, 6 }
|
local levels_for_cleared_rows = { 1, 2, 4, 6 }
|
||||||
@@ -165,7 +165,9 @@ function Marathon2020Game:onPieceEnter()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:whilePieceActive()
|
function Marathon2020Game:whilePieceActive()
|
||||||
self.grade_point_decay_counter = self.grade_point_decay_counter + self.grade + 2
|
if not self.clear then
|
||||||
|
self.grade_point_decay_counter = self.grade_point_decay_counter + self.grade + 2
|
||||||
|
end
|
||||||
if self.grade_point_decay_counter > 240 then
|
if self.grade_point_decay_counter > 240 then
|
||||||
self.grade_point_decay_counter = 0
|
self.grade_point_decay_counter = 0
|
||||||
self.grade_points = math.max(0, self.grade_points - 1)
|
self.grade_points = math.max(0, self.grade_points - 1)
|
||||||
@@ -282,11 +284,11 @@ function Marathon2020Game:sectionPassed(old_level, new_level)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:checkTorikan(section)
|
function Marathon2020Game:checkTorikan(section)
|
||||||
if section == 5 and self.frames < sp(6,00,00) then self.torikan_passed[500] = true end
|
if section == 5 and self.frames < frameTime(6,00,00) then self.torikan_passed[500] = true end
|
||||||
if section == 9 and self.frames < sp(8,30,00) then self.torikan_passed[900] = true end
|
if section == 9 and self.frames < frameTime(8,30,00) then self.torikan_passed[900] = true end
|
||||||
if section == 10 and self.frames < sp(8,45,00) then self.torikan_passed[1000] = true end
|
if section == 10 and self.frames < frameTime(8,45,00) then self.torikan_passed[1000] = true end
|
||||||
if section == 15 and self.frames < sp(11,30,00) then self.torikan_passed[1500] = true end
|
if section == 15 and self.frames < frameTime(11,30,00) then self.torikan_passed[1500] = true end
|
||||||
if section == 19 and self.frames < sp(13,15,00) then self.torikan_passed[1900] = true end
|
if section == 19 and self.frames < frameTime(13,15,00) then self.torikan_passed[1900] = true end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:checkClear(level)
|
function Marathon2020Game:checkClear(level)
|
||||||
@@ -375,19 +377,29 @@ function Marathon2020Game:updateScore(level, drop_bonus, cleared_lines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Marathon2020Game.mRollOpacityFunction = function(age)
|
Marathon2020Game.rollOpacityFunction = function(age)
|
||||||
if age > 300 then return 0
|
if age > 300 then return 0
|
||||||
elseif age < 240 then return 1
|
elseif age < 240 then return 1
|
||||||
else return (300 - age) / 60 end
|
else return (300 - age) / 60 end
|
||||||
end
|
end
|
||||||
|
|
||||||
Marathon2020Game.rollOpacityFunction = function(age)
|
Marathon2020Game.mRollOpacityFunction = function(age)
|
||||||
if age > 4 then return 0
|
if age > 4 then return 0
|
||||||
else return 1 - age / 4 end
|
else return 1 - age / 4 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:qualifiesForMRoll()
|
function Marathon2020Game:qualifiesForMRoll()
|
||||||
return false -- until I actually have grading working
|
return false -- until I actually have grading working
|
||||||
|
--[[
|
||||||
|
|
||||||
|
GM-roll requirements
|
||||||
|
|
||||||
|
You qualify for the GM roll if you:
|
||||||
|
- Reach level 2020
|
||||||
|
- with a grade of 50
|
||||||
|
- in less than 13:30.00 total.
|
||||||
|
|
||||||
|
]]--
|
||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:drawGrid()
|
function Marathon2020Game:drawGrid()
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ MarathonA1Game.name = "Marathon A1"
|
|||||||
MarathonA1Game.hash = "MarathonA1"
|
MarathonA1Game.hash = "MarathonA1"
|
||||||
MarathonA1Game.tagline = "Can you score enough points to reach the title of Grand Master?"
|
MarathonA1Game.tagline = "Can you score enough points to reach the title of Grand Master?"
|
||||||
|
|
||||||
MarathonA1Game.arr = 1
|
|
||||||
MarathonA1Game.drop_speed = 1
|
|
||||||
|
|
||||||
function MarathonA1Game:new()
|
function MarathonA1Game:new()
|
||||||
MarathonA1Game.super:new()
|
MarathonA1Game.super:new()
|
||||||
@@ -24,6 +23,11 @@ function MarathonA1Game:new()
|
|||||||
level500 = false,
|
level500 = false,
|
||||||
level999 = false
|
level999 = false
|
||||||
}
|
}
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.randomizer = History4RollsRandomizer()
|
self.randomizer = History4RollsRandomizer()
|
||||||
|
|
||||||
@@ -34,11 +38,11 @@ function MarathonA1Game:new()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getARE()
|
function MarathonA1Game:getARE()
|
||||||
return 25
|
return 30
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getLineARE()
|
function MarathonA1Game:getLineARE()
|
||||||
return 25
|
return 27
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getDasLimit()
|
function MarathonA1Game:getDasLimit()
|
||||||
@@ -46,7 +50,7 @@ function MarathonA1Game:getDasLimit()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getLineClearDelay()
|
function MarathonA1Game:getLineClearDelay()
|
||||||
return 40
|
return 44
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getLockDelay()
|
function MarathonA1Game:getLockDelay()
|
||||||
@@ -142,12 +146,12 @@ end
|
|||||||
|
|
||||||
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
|
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
if cleared_lines > 0 then
|
if cleared_lines > 0 then
|
||||||
|
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||||
self.score = self.score + (
|
self.score = self.score + (
|
||||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||||
cleared_lines * (cleared_lines * 2 - 1) * self.combo
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
self.lines = self.lines + cleared_lines
|
||||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
self.drop_bonus = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
@@ -156,15 +160,15 @@ end
|
|||||||
|
|
||||||
function MarathonA1Game:checkGMRequirements(old_level, new_level)
|
function MarathonA1Game:checkGMRequirements(old_level, new_level)
|
||||||
if old_level < 300 and new_level >= 300 then
|
if old_level < 300 and new_level >= 300 then
|
||||||
if self.score > 12000 and self.frames <= sp(4,15) then
|
if self.score > 12000 and self.frames <= frameTime(4,15) then
|
||||||
self.gm_conditions["level300"] = true
|
self.gm_conditions["level300"] = true
|
||||||
end
|
end
|
||||||
elseif old_level < 500 and new_level >= 500 then
|
elseif old_level < 500 and new_level >= 500 then
|
||||||
if self.score > 40000 and self.frames <= sp(7,30) then
|
if self.score > 40000 and self.frames <= frameTime(7,30) then
|
||||||
self.gm_conditions["level500"] = true
|
self.gm_conditions["level500"] = true
|
||||||
end
|
end
|
||||||
elseif old_level < 999 and new_level >= 999 then
|
elseif old_level < 999 and new_level >= 999 then
|
||||||
if self.score > 126000 and self.frames <= sp(13,30) then
|
if self.score > 126000 and self.frames <= frameTime(13,30) then
|
||||||
self.gm_conditions["level900"] = true
|
self.gm_conditions["level900"] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -172,6 +176,9 @@ end
|
|||||||
|
|
||||||
function MarathonA1Game:drawGrid()
|
function MarathonA1Game:drawGrid()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:drawScoringInfo()
|
function MarathonA1Game:drawScoringInfo()
|
||||||
@@ -182,13 +189,17 @@ function MarathonA1Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
||||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
@@ -200,13 +211,16 @@ function MarathonA1Game:drawScoringInfo()
|
|||||||
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
||||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_8x11)
|
love.graphics.setFont(font_8x11)
|
||||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:getSectionEndLevel()
|
function MarathonA1Game:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ MarathonA2Game.name = "Marathon A2"
|
|||||||
MarathonA2Game.hash = "MarathonA2"
|
MarathonA2Game.hash = "MarathonA2"
|
||||||
MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible roll?"
|
MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible roll?"
|
||||||
|
|
||||||
MarathonA2Game.arr = 1
|
|
||||||
MarathonA2Game.drop_speed = 1
|
|
||||||
|
|
||||||
function MarathonA2Game:new()
|
function MarathonA2Game:new()
|
||||||
MarathonA2Game.super:new()
|
MarathonA2Game.super:new()
|
||||||
@@ -27,6 +27,12 @@ function MarathonA2Game:new()
|
|||||||
self.section_times = { [0] = 0 }
|
self.section_times = { [0] = 0 }
|
||||||
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
self.lock_drop = false
|
self.lock_drop = false
|
||||||
@@ -35,16 +41,16 @@ function MarathonA2Game:new()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA2Game:getARE()
|
function MarathonA2Game:getARE()
|
||||||
if self.level < 700 then return 25
|
if self.level < 700 then return 27
|
||||||
elseif self.level < 800 then return 16
|
elseif self.level < 800 then return 18
|
||||||
else return 12 end
|
else return 14 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA2Game:getLineARE()
|
function MarathonA2Game:getLineARE()
|
||||||
if self.level < 600 then return 25
|
if self.level < 600 then return 27
|
||||||
elseif self.level < 700 then return 16
|
elseif self.level < 700 then return 18
|
||||||
elseif self.level < 800 then return 12
|
elseif self.level < 800 then return 14
|
||||||
else return 6 end
|
else return 8 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA2Game:getDasLimit()
|
function MarathonA2Game:getDasLimit()
|
||||||
@@ -260,7 +266,7 @@ function MarathonA2Game:qualifiesForMRoll()
|
|||||||
local section_average = 0
|
local section_average = 0
|
||||||
for section = 0, 4 do
|
for section = 0, 4 do
|
||||||
section_average = section_average + self.section_times[section]
|
section_average = section_average + self.section_times[section]
|
||||||
if self.section_times[section] > sp(1,05) then
|
if self.section_times[section] > frameTime(1,05) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -273,7 +279,7 @@ function MarathonA2Game:qualifiesForMRoll()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.grade < 17 or self.frames > sp(8,45) then
|
if self.grade < 17 or self.frames > frameTime(9,30) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -325,18 +331,25 @@ function MarathonA2Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_8x11)
|
love.graphics.setFont(font_8x11)
|
||||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
@@ -352,7 +365,7 @@ function MarathonA2Game:getHighscoreData()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA2Game:getSectionEndLevel()
|
function MarathonA2Game:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ MarathonA3Game.name = "Marathon A3"
|
|||||||
MarathonA3Game.hash = "MarathonA3"
|
MarathonA3Game.hash = "MarathonA3"
|
||||||
MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all the Section COOLs?"
|
MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all the Section COOLs?"
|
||||||
|
|
||||||
MarathonA3Game.arr = 1
|
|
||||||
MarathonA3Game.drop_speed = 1
|
|
||||||
|
|
||||||
function MarathonA3Game:new()
|
function MarathonA3Game:new()
|
||||||
MarathonA3Game.super:new()
|
MarathonA3Game.super:new()
|
||||||
@@ -36,6 +36,9 @@ function MarathonA3Game:new()
|
|||||||
self.lock_hard_drop = true
|
self.lock_hard_drop = true
|
||||||
self.enable_hold = true
|
self.enable_hold = true
|
||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
|
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA3Game:getARE()
|
function MarathonA3Game:getARE()
|
||||||
@@ -67,7 +70,9 @@ function MarathonA3Game:getLineClearDelay()
|
|||||||
elseif self.speed_level < 600 then return 25
|
elseif self.speed_level < 600 then return 25
|
||||||
elseif self.speed_level < 700 then return 16
|
elseif self.speed_level < 700 then return 16
|
||||||
elseif self.speed_level < 800 then return 12
|
elseif self.speed_level < 800 then return 12
|
||||||
else return 6 end
|
elseif self.speed_level < 1100 then return 6
|
||||||
|
elseif self.speed_level < 1200 then return 5
|
||||||
|
else return 4 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA3Game:getLockDelay()
|
function MarathonA3Game:getLockDelay()
|
||||||
@@ -117,7 +122,7 @@ function MarathonA3Game:advanceOneFrame()
|
|||||||
if self.roll_frames + 1 == 0 then
|
if self.roll_frames + 1 == 0 then
|
||||||
switchBGM("credit_roll", "gm3")
|
switchBGM("credit_roll", "gm3")
|
||||||
end
|
end
|
||||||
return
|
return false
|
||||||
elseif self.roll_frames > 3238 then
|
elseif self.roll_frames > 3238 then
|
||||||
if self:qualifiesForMRoll() then
|
if self:qualifiesForMRoll() then
|
||||||
self.roll_points = self.roll_points + 160
|
self.roll_points = self.roll_points + 160
|
||||||
@@ -158,13 +163,13 @@ function MarathonA3Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local cool_cutoffs = {
|
local cool_cutoffs = {
|
||||||
sp(0,52), sp(0,52), sp(0,49), sp(0,45), sp(0,45),
|
frameTime(0,52), frameTime(0,52), frameTime(0,49), frameTime(0,45), frameTime(0,45),
|
||||||
sp(0,42), sp(0,42), sp(0,38), sp(0,38),
|
frameTime(0,42), frameTime(0,42), frameTime(0,38), frameTime(0,38),
|
||||||
}
|
}
|
||||||
|
|
||||||
local regret_cutoffs = {
|
local regret_cutoffs = {
|
||||||
sp(0,90), sp(0,75), sp(0,75), sp(0,68), sp(0,60),
|
frameTime(0,90), frameTime(0,75), frameTime(0,75), frameTime(0,68), frameTime(0,60),
|
||||||
sp(0,60), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
|
frameTime(0,60), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50),
|
||||||
}
|
}
|
||||||
|
|
||||||
function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
||||||
@@ -180,17 +185,23 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
|||||||
if section_time > regret_cutoffs[section] then
|
if section_time > regret_cutoffs[section] then
|
||||||
self.section_cool_grade = self.section_cool_grade - 1
|
self.section_cool_grade = self.section_cool_grade - 1
|
||||||
table.insert(self.section_status, "regret")
|
table.insert(self.section_status, "regret")
|
||||||
|
self.coolregret_message = "REGRET!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
elseif section <= 9 and self.section_status[section - 1] == "cool" and
|
elseif section <= 9 and self.section_status[section - 1] == "cool" and
|
||||||
self.section_70_times[section] < self.section_70_times[section - 1] + 1000 then
|
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then
|
||||||
self.section_cool_grade = self.section_cool_grade + 1
|
self.section_cool_grade = self.section_cool_grade + 1
|
||||||
self.speed_level = self.speed_level + 100
|
self.speed_level = self.speed_level + 100
|
||||||
table.insert(self.section_status, "cool")
|
table.insert(self.section_status, "cool")
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
elseif self.section_status[section - 1] == "cool" then
|
elseif self.section_status[section - 1] == "cool" then
|
||||||
table.insert(self.section_status, "none")
|
table.insert(self.section_status, "none")
|
||||||
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
|
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
|
||||||
self.section_cool_grade = self.section_cool_grade + 1
|
self.section_cool_grade = self.section_cool_grade + 1
|
||||||
self.speed_level = self.speed_level + 100
|
self.speed_level = self.speed_level + 100
|
||||||
table.insert(self.section_status, "cool")
|
table.insert(self.section_status, "cool")
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
else
|
else
|
||||||
table.insert(self.section_status, "none")
|
table.insert(self.section_status, "none")
|
||||||
end
|
end
|
||||||
@@ -346,6 +357,9 @@ function MarathonA3Game:drawGrid()
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -367,12 +381,16 @@ function MarathonA3Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
-- draw section time data
|
-- draw section time data
|
||||||
current_section = math.floor(self.level / 100) + 1
|
current_section = math.floor(self.level / 100) + 1
|
||||||
@@ -400,13 +418,21 @@ function MarathonA3Game:drawScoringInfo()
|
|||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||||
|
|
||||||
|
if(self.coolregret_timer > 0) then
|
||||||
|
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||||
|
self.coolregret_timer = self.coolregret_timer - 1
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_8x11)
|
love.graphics.setFont(font_8x11)
|
||||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
end
|
end
|
||||||
@@ -420,7 +446,7 @@ function MarathonA3Game:getHighscoreData()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA3Game:getSectionEndLevel()
|
function MarathonA3Game:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,22 +3,20 @@ require 'funcs'
|
|||||||
local GameMode = require 'tetris.modes.gamemode'
|
local GameMode = require 'tetris.modes.gamemode'
|
||||||
local Piece = require 'tetris.components.piece'
|
local Piece = require 'tetris.components.piece'
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart'
|
||||||
|
|
||||||
local MarathonL1Game = GameMode:extend()
|
local MarathonAX4Game = GameMode:extend()
|
||||||
|
|
||||||
MarathonL1Game.name = "Line Attack"
|
MarathonAX4Game.name = "Marathon AX4"
|
||||||
MarathonL1Game.hash = "MarathonL1"
|
MarathonAX4Game.hash = "MarathonAX4"
|
||||||
MarathonL1Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
MarathonAX4Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||||
|
|
||||||
MarathonL1Game.arr = 1
|
|
||||||
MarathonL1Game.drop_speed = 1
|
|
||||||
|
|
||||||
function MarathonL1Game:new()
|
function MarathonAX4Game:new()
|
||||||
MarathonL1Game.super:new()
|
MarathonAX4Game.super:new()
|
||||||
|
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = Bag7NoSZOStartRandomizer()
|
||||||
|
|
||||||
self.section_time_limit = 3600
|
self.section_time_limit = 3600
|
||||||
self.section_start_time = 0
|
self.section_start_time = 0
|
||||||
@@ -30,7 +28,7 @@ function MarathonL1Game:new()
|
|||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getARE()
|
function MarathonAX4Game:getARE()
|
||||||
if self.lines < 10 then return 18
|
if self.lines < 10 then return 18
|
||||||
elseif self.lines < 40 then return 14
|
elseif self.lines < 40 then return 14
|
||||||
elseif self.lines < 60 then return 12
|
elseif self.lines < 60 then return 12
|
||||||
@@ -40,24 +38,24 @@ function MarathonL1Game:getARE()
|
|||||||
else return 6 end
|
else return 6 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getLineARE()
|
function MarathonAX4Game:getLineARE()
|
||||||
return self:getARE()
|
return self:getARE()
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getDasLimit()
|
function MarathonAX4Game:getDasLimit()
|
||||||
if self.lines < 20 then return 10
|
if self.lines < 20 then return 10
|
||||||
elseif self.lines < 50 then return 9
|
elseif self.lines < 50 then return 9
|
||||||
elseif self.lines < 70 then return 8
|
elseif self.lines < 70 then return 8
|
||||||
else return 7 end
|
else return 7 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getLineClearDelay()
|
function MarathonAX4Game:getLineClearDelay()
|
||||||
if self.lines < 10 then return 14
|
if self.lines < 10 then return 14
|
||||||
elseif self.lines < 30 then return 9
|
elseif self.lines < 30 then return 9
|
||||||
else return 5 end
|
else return 5 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getLockDelay()
|
function MarathonAX4Game:getLockDelay()
|
||||||
if self.lines < 10 then return 28
|
if self.lines < 10 then return 28
|
||||||
elseif self.lines < 20 then return 24
|
elseif self.lines < 20 then return 24
|
||||||
elseif self.lines < 30 then return 22
|
elseif self.lines < 30 then return 22
|
||||||
@@ -67,15 +65,15 @@ function MarathonL1Game:getLockDelay()
|
|||||||
else return 13 end
|
else return 13 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getGravity()
|
function MarathonAX4Game:getGravity()
|
||||||
return 20
|
return 20
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getSection()
|
function MarathonAX4Game:getSection()
|
||||||
return math.floor(level / 100) + 1
|
return math.floor(level / 100) + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:advanceOneFrame()
|
function MarathonAX4Game:advanceOneFrame()
|
||||||
if self.clear then
|
if self.clear then
|
||||||
self.roll_frames = self.roll_frames + 1
|
self.roll_frames = self.roll_frames + 1
|
||||||
if self.roll_frames < 0 then
|
if self.roll_frames < 0 then
|
||||||
@@ -94,7 +92,7 @@ function MarathonL1Game:advanceOneFrame()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:onLineClear(cleared_row_count)
|
function MarathonAX4Game:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
local new_lines = self.lines + cleared_row_count
|
local new_lines = self.lines + cleared_row_count
|
||||||
self:updateSectionTimes(self.lines, new_lines)
|
self:updateSectionTimes(self.lines, new_lines)
|
||||||
@@ -106,11 +104,11 @@ function MarathonL1Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getSectionTime()
|
function MarathonAX4Game:getSectionTime()
|
||||||
return self.frames - self.section_start_time
|
return self.frames - self.section_start_time
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:updateSectionTimes(old_lines, new_lines)
|
function MarathonAX4Game:updateSectionTimes(old_lines, new_lines)
|
||||||
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
|
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
|
||||||
-- record new section
|
-- record new section
|
||||||
table.insert(self.section_times, self:getSectionTime())
|
table.insert(self.section_times, self:getSectionTime())
|
||||||
@@ -119,23 +117,23 @@ function MarathonL1Game:updateSectionTimes(old_lines, new_lines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:onPieceEnter()
|
function MarathonAX4Game:onPieceEnter()
|
||||||
self.section_clear = false
|
self.section_clear = false
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:drawGrid(ruleset)
|
function MarathonAX4Game:drawGrid(ruleset)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getHighscoreData()
|
function MarathonAX4Game:getHighscoreData()
|
||||||
return {
|
return {
|
||||||
lines = self.lines,
|
lines = self.lines,
|
||||||
frames = self.frames,
|
frames = self.frames,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:drawScoringInfo()
|
function MarathonAX4Game:drawScoringInfo()
|
||||||
MarathonL1Game.super.drawScoringInfo(self)
|
MarathonAX4Game.super.drawScoringInfo(self)
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
@@ -143,7 +141,7 @@ function MarathonL1Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||||
@@ -158,19 +156,19 @@ function MarathonL1Game:drawScoringInfo()
|
|||||||
|
|
||||||
-- draw time left, flash red if necessary
|
-- draw time left, flash red if necessary
|
||||||
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
||||||
if not self.game_over and not self.clear and time_left < sp(0,10) and time_left % 4 < 2 then
|
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
|
||||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||||
end
|
end
|
||||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getSectionEndLines()
|
function MarathonAX4Game:getSectionEndLines()
|
||||||
return math.floor(self.lines / 10 + 1) * 10
|
return math.floor(self.lines / 10 + 1) * 10
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonL1Game:getBackground()
|
function MarathonAX4Game:getBackground()
|
||||||
return math.floor(self.lines / 10)
|
return math.floor(self.lines / 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
return MarathonL1Game
|
return MarathonAX4Game
|
||||||
185
tetris/modes/marathon_c89.lua
Normal file
185
tetris/modes/marathon_c89.lua
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
require 'funcs'
|
||||||
|
|
||||||
|
local GameMode = require 'tetris.modes.gamemode'
|
||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
|
||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local MarathonC89Game = GameMode:extend()
|
||||||
|
|
||||||
|
MarathonC89Game.name = "Marathon C89"
|
||||||
|
MarathonC89Game.hash = "MarathonC89"
|
||||||
|
MarathonC89Game.tagline = "Can you play fast enough to reach the killscreen?"
|
||||||
|
|
||||||
|
|
||||||
|
function MarathonC89Game:new()
|
||||||
|
MarathonC89Game.super:new()
|
||||||
|
|
||||||
|
self.randomizer = Randomizer()
|
||||||
|
|
||||||
|
self.ready_frames = 1
|
||||||
|
self.waiting_frames = 72
|
||||||
|
|
||||||
|
self.start_level = 12
|
||||||
|
self.level = 12
|
||||||
|
|
||||||
|
self.lock_drop = true
|
||||||
|
self.enable_hard_drop = false
|
||||||
|
self.enable_hold = false
|
||||||
|
self.next_queue_length = 1
|
||||||
|
self.additive_gravity = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:getDropSpeed() return 1/2 end
|
||||||
|
function MarathonC89Game:getDasLimit() return 16 end
|
||||||
|
function MarathonC89Game:getARR() return 6 end
|
||||||
|
|
||||||
|
function MarathonC89Game:getARE() return 6 end
|
||||||
|
function MarathonC89Game:getLineARE() return 6 end
|
||||||
|
function MarathonC89Game:getLineClearDelay() return 30 end
|
||||||
|
function MarathonC89Game:getLockDelay() return 0 end
|
||||||
|
|
||||||
|
function MarathonC89Game:chargeDAS(inputs)
|
||||||
|
if inputs[self.das.direction] == true and
|
||||||
|
self.prev_inputs[self.das.direction] == true and
|
||||||
|
not inputs["down"] and
|
||||||
|
self.piece ~= nil
|
||||||
|
then
|
||||||
|
local das_frames = self.das.frames + 1
|
||||||
|
if das_frames >= self:getDasLimit() then
|
||||||
|
if self.das.direction == "left" then
|
||||||
|
self.move = (self:getARR() == 0 and "speed" or "") .. "left"
|
||||||
|
self.das.frames = self:getDasLimit() - self:getARR()
|
||||||
|
elseif self.das.direction == "right" then
|
||||||
|
self.move = (self:getARR() == 0 and "speed" or "") .. "right"
|
||||||
|
self.das.frames = self:getDasLimit() - self:getARR()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.move = "none"
|
||||||
|
self.das.frames = das_frames
|
||||||
|
end
|
||||||
|
elseif inputs["right"] == true then
|
||||||
|
self.das.direction = "right"
|
||||||
|
if not inputs["down"] and self.piece ~= nil then
|
||||||
|
self.move = "right"
|
||||||
|
self.das.frames = 0
|
||||||
|
else
|
||||||
|
self.move = "none"
|
||||||
|
end
|
||||||
|
elseif inputs["left"] == true then
|
||||||
|
self.das.direction = "left"
|
||||||
|
if not inputs["down"] and self.piece ~= nil then
|
||||||
|
self.move = "left"
|
||||||
|
self.das.frames = 0
|
||||||
|
else
|
||||||
|
self.move = "none"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.move = "none"
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.das.direction == "left" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=-1, y=0}) or
|
||||||
|
self.das.direction == "right" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=1, y=0})
|
||||||
|
then
|
||||||
|
self.das.frames = self:getDasLimit()
|
||||||
|
end
|
||||||
|
|
||||||
|
if inputs["down"] == false and self.prev_inputs["down"] == true then
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local gravity_table = {
|
||||||
|
[0] =
|
||||||
|
1366/65536, 1525/65536, 1725/65536, 1986/65536, 2341/65536,
|
||||||
|
2850/65536, 3641/65536, 5042/65536, 8192/65536, 10923/65536,
|
||||||
|
13108/65536, 13108/65536, 13108/65536, 16384/65536, 16384/65536,
|
||||||
|
16384/65536, 21846/65536, 21846/65536, 21846/65536
|
||||||
|
}
|
||||||
|
|
||||||
|
function MarathonC89Game:getGravity()
|
||||||
|
if self.waiting_frames > 0 then return 0 end
|
||||||
|
if self.level >= 29 then return 1
|
||||||
|
elseif self.level >= 19 then return 1/2
|
||||||
|
else return gravity_table[self.level] end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:advanceOneFrame()
|
||||||
|
if self.waiting_frames > 0 then
|
||||||
|
self.waiting_frames = self.waiting_frames - 1
|
||||||
|
else
|
||||||
|
self.frames = self.frames + 1
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:onPieceLock()
|
||||||
|
self.score = self.score + self.drop_bonus
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local cleared_line_scores = { 40, 100, 300, 1200 }
|
||||||
|
|
||||||
|
function MarathonC89Game:getLevelForLines()
|
||||||
|
if self.start_level < 10 then
|
||||||
|
return math.max(self.start_level, math.floor(self.lines / 10))
|
||||||
|
elseif self.start_level < 16 then
|
||||||
|
return math.max(self.start_level, self.start_level + math.floor((self.lines - 100) / 10))
|
||||||
|
else
|
||||||
|
return math.max(self.start_level, math.floor((self.lines - 60) / 10))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if cleared_lines > 0 then
|
||||||
|
self.score = self.score + cleared_line_scores[cleared_lines] * (self.level + 1)
|
||||||
|
self.lines = self.lines + cleared_lines
|
||||||
|
self.level = self:getLevelForLines()
|
||||||
|
else
|
||||||
|
self.drop_bonus = 0
|
||||||
|
self.combo = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:drawGrid()
|
||||||
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:drawScoringInfo()
|
||||||
|
MarathonC89Game.super.drawScoringInfo(self)
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
love.graphics.print(
|
||||||
|
self.das.direction .. " " ..
|
||||||
|
self.das.frames .. " " ..
|
||||||
|
strTrueValues(self.prev_inputs)
|
||||||
|
)
|
||||||
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
|
love.graphics.printf("LINES", 240, 120, 40, "left")
|
||||||
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_3)
|
||||||
|
love.graphics.printf(self.lines, 240, 140, 90, "left")
|
||||||
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_8x11)
|
||||||
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function MarathonC89Game:getBackground()
|
||||||
|
return math.min(self.level, 19)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonC89Game:getHighscoreData()
|
||||||
|
return {
|
||||||
|
score = self.score,
|
||||||
|
level = self.level,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return MarathonC89Game
|
||||||
@@ -11,8 +11,8 @@ PacerTest.name = "TetrisGram™ Pacer Test"
|
|||||||
PacerTest.hash = "PacerTest"
|
PacerTest.hash = "PacerTest"
|
||||||
PacerTest.tagline = ""
|
PacerTest.tagline = ""
|
||||||
|
|
||||||
PacerTest.arr = 1
|
|
||||||
PacerTest.drop_speed = 1
|
|
||||||
|
|
||||||
local function getLevelFrames(level)
|
local function getLevelFrames(level)
|
||||||
if level == 1 then return 72 * 60 / 8.0
|
if level == 1 then return 72 * 60 / 8.0
|
||||||
@@ -113,7 +113,7 @@ function PacerTest:checkSectionStatus()
|
|||||||
self.section_clear = false
|
self.section_clear = false
|
||||||
else
|
else
|
||||||
self.strikes = self.strikes + 1
|
self.strikes = self.strikes + 1
|
||||||
if self.strikes >= 200 then
|
if self.strikes >= 2 then
|
||||||
self.game_over = true
|
self.game_over = true
|
||||||
fadeoutBGM(2.5)
|
fadeoutBGM(2.5)
|
||||||
end
|
end
|
||||||
@@ -164,7 +164,7 @@ function PacerTest:drawScoringInfo()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PacerTest:getBackground()
|
function PacerTest:getBackground()
|
||||||
return self.level - 1
|
return math.min(self.level - 1, 19)
|
||||||
end
|
end
|
||||||
|
|
||||||
return PacerTest
|
return PacerTest
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ PhantomManiaGame.name = "Phantom Mania"
|
|||||||
PhantomManiaGame.hash = "PhantomMania"
|
PhantomManiaGame.hash = "PhantomMania"
|
||||||
PhantomManiaGame.tagline = "The blocks disappear as soon as they're locked! Can you remember where everything is?"
|
PhantomManiaGame.tagline = "The blocks disappear as soon as they're locked! Can you remember where everything is?"
|
||||||
|
|
||||||
PhantomManiaGame.arr = 1
|
|
||||||
PhantomManiaGame.drop_speed = 1
|
|
||||||
|
|
||||||
function PhantomManiaGame:new()
|
function PhantomManiaGame:new()
|
||||||
PhantomManiaGame.super:new()
|
PhantomManiaGame.super:new()
|
||||||
|
|
||||||
@@ -64,15 +61,15 @@ function PhantomManiaGame:getGravity()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomManiaGame:hitTorikan(old_level, new_level)
|
function PhantomManiaGame:hitTorikan(old_level, new_level)
|
||||||
if old_level < 300 and new_level >= 300 and self.frames > sp(2,28) then
|
if old_level < 300 and new_level >= 300 and self.frames > frameTime(2,28) then
|
||||||
self.level = 300
|
self.level = 300
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,38) then
|
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,38) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 800 and new_level >= 800 and self.frames > sp(5,23) then
|
if old_level < 800 and new_level >= 800 and self.frames > frameTime(5,23) then
|
||||||
self.level = 800
|
self.level = 800
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -102,7 +99,7 @@ end
|
|||||||
function PhantomManiaGame:onLineClear(cleared_row_count)
|
function PhantomManiaGame:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
local new_level = self.level + cleared_row_count
|
local new_level = self.level + cleared_row_count
|
||||||
if self:hitTorikan(self.level, new_level) then
|
if new_level >= 999 or self:hitTorikan(self.level, new_level) then
|
||||||
if new_level >= 999 then
|
if new_level >= 999 then
|
||||||
self.level = 999
|
self.level = 999
|
||||||
end
|
end
|
||||||
@@ -181,7 +178,7 @@ function PhantomManiaGame:drawScoringInfo()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomManiaGame:getSectionEndLevel()
|
function PhantomManiaGame:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ PhantomMania2Game.name = "Phantom Mania 2"
|
|||||||
PhantomMania2Game.hash = "PhantomMania2"
|
PhantomMania2Game.hash = "PhantomMania2"
|
||||||
PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make it to level 1300?"
|
PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make it to level 1300?"
|
||||||
|
|
||||||
PhantomMania2Game.arr = 1
|
|
||||||
PhantomMania2Game.drop_speed = 1
|
|
||||||
|
|
||||||
function PhantomMania2Game:new()
|
function PhantomMania2Game:new()
|
||||||
PhantomMania2Game.super:new()
|
PhantomMania2Game.super:new()
|
||||||
@@ -25,6 +25,8 @@ function PhantomMania2Game:new()
|
|||||||
self.combo = 1
|
self.combo = 1
|
||||||
self.hold_age = 0
|
self.hold_age = 0
|
||||||
self.queue_age = 0
|
self.queue_age = 0
|
||||||
|
self.roll_points = 0
|
||||||
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_drop = true
|
||||||
@@ -84,19 +86,19 @@ function PhantomMania2Game:getNextPiece(ruleset)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:hitTorikan(old_level, new_level)
|
function PhantomMania2Game:hitTorikan(old_level, new_level)
|
||||||
if old_level < 300 and new_level >= 300 and self.frames > sp(2,02) then
|
if old_level < 300 and new_level >= 300 and self.frames > frameTime(2,02) then
|
||||||
self.level = 300
|
self.level = 300
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,03) then
|
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,03) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 800 and new_level >= 800 and self.frames > sp(4,40) then
|
if old_level < 800 and new_level >= 800 and self.frames > frameTime(4,45) then
|
||||||
self.level = 800
|
self.level = 800
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,38) then
|
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(5,38) then
|
||||||
self.level = 1000
|
self.level = 1000
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -135,7 +137,7 @@ function PhantomMania2Game:onPieceEnter()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local cleared_row_levels = {1, 2, 4, 6}
|
local cleared_row_levels = {1, 2, 4, 6}
|
||||||
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
|
local cleared_row_points = {2, 6, 15, 40}
|
||||||
|
|
||||||
function PhantomMania2Game:onLineClear(cleared_row_count)
|
function PhantomMania2Game:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
@@ -144,6 +146,7 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
|
|||||||
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
||||||
if new_level >= 1300 then
|
if new_level >= 1300 then
|
||||||
self.level = 1300
|
self.level = 1300
|
||||||
|
self.big_mode = true
|
||||||
end
|
end
|
||||||
self.clear = true
|
self.clear = true
|
||||||
self.grid:clear()
|
self.grid:clear()
|
||||||
@@ -152,11 +155,17 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
|
|||||||
self.level = math.min(new_level, 1300)
|
self.level = math.min(new_level, 1300)
|
||||||
end
|
end
|
||||||
self:advanceBottomRow(-cleared_row_count)
|
self:advanceBottomRow(-cleared_row_count)
|
||||||
|
else
|
||||||
|
self.roll_points = self.roll_points + cleared_row_points[cleared_row_count / 2]
|
||||||
|
if self.roll_points >= 100 then
|
||||||
|
self.roll_points = self.roll_points - 100
|
||||||
|
self.grade = self.grade + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:onPieceLock()
|
function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
|
||||||
self:advanceBottomRow(1)
|
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:onHold()
|
function PhantomMania2Game:onHold()
|
||||||
@@ -179,15 +188,15 @@ end
|
|||||||
|
|
||||||
|
|
||||||
local cool_cutoffs = {
|
local cool_cutoffs = {
|
||||||
sp(0,36), sp(0,36), sp(0,36), sp(0,36), sp(0,36),
|
frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36),
|
||||||
sp(0,30), sp(0,30), sp(0,30), sp(0,30), sp(0,30),
|
frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30),
|
||||||
sp(0,27), sp(0,27), sp(0,27),
|
frameTime(0,27), frameTime(0,27), frameTime(0,27),
|
||||||
}
|
}
|
||||||
|
|
||||||
local regret_cutoffs = {
|
local regret_cutoffs = {
|
||||||
sp(0,50), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
|
frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50),
|
||||||
sp(0,40), sp(0,40), sp(0,40), sp(0,40), sp(0,40),
|
frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40),
|
||||||
sp(0,35), sp(0,35), sp(0,35),
|
frameTime(0,35), frameTime(0,35), frameTime(0,35),
|
||||||
}
|
}
|
||||||
|
|
||||||
function PhantomMania2Game:updateSectionTimes(old_level, new_level)
|
function PhantomMania2Game:updateSectionTimes(old_level, new_level)
|
||||||
@@ -225,7 +234,7 @@ PhantomMania2Game.garbageOpacityFunction = function(age)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:drawGrid()
|
function PhantomMania2Game:drawGrid()
|
||||||
if not (self.game_over or self.clear) then
|
if not (self.game_over or (self.clear and self.level < 1300)) then
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
||||||
else
|
else
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
@@ -243,7 +252,7 @@ local function getLetterGrade(grade)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:setNextOpacity(i)
|
function PhantomMania2Game:setNextOpacity(i)
|
||||||
if self.level > 1000 then
|
if self.level > 1000 and self.level < 1300 then
|
||||||
local hidden_next_pieces = math.floor(self.level / 100) - 10
|
local hidden_next_pieces = math.floor(self.level / 100) - 10
|
||||||
if i < hidden_next_pieces then
|
if i < hidden_next_pieces then
|
||||||
love.graphics.setColor(1, 1, 1, 0)
|
love.graphics.setColor(1, 1, 1, 0)
|
||||||
@@ -258,7 +267,7 @@ function PhantomMania2Game:setNextOpacity(i)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:setHoldOpacity()
|
function PhantomMania2Game:setHoldOpacity()
|
||||||
if self.level > 1000 then
|
if self.level > 1000 and self.level < 1300 then
|
||||||
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.hold_age / 15))
|
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.hold_age / 15))
|
||||||
else
|
else
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|||||||
@@ -1,200 +1,16 @@
|
|||||||
require 'funcs'
|
local PhantomManiaGame = require 'tetris.modes.phantom_mania'
|
||||||
|
|
||||||
local GameMode = require 'tetris.modes.gamemode'
|
local PhantomManiaNGame = PhantomManiaGame:extend()
|
||||||
local Piece = require 'tetris.components.piece'
|
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
PhantomManiaNGame.name = "Phantom Mania N"
|
||||||
|
PhantomManiaNGame.hash = "PhantomManiaN"
|
||||||
|
PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS support."
|
||||||
|
|
||||||
local PhantomManiaGame = GameMode:extend()
|
function PhantomManiaNGame:new()
|
||||||
|
PhantomManiaNGame.super:new()
|
||||||
|
|
||||||
PhantomManiaGame.name = "Phantom Mania N"
|
|
||||||
PhantomManiaGame.hash = "PhantomManiaN"
|
|
||||||
PhantomManiaGame.tagline = "The old mode from Nullpomino."
|
|
||||||
|
|
||||||
PhantomManiaGame.arr = 1
|
|
||||||
PhantomManiaGame.drop_speed = 1
|
|
||||||
|
|
||||||
function PhantomManiaGame:new()
|
|
||||||
PhantomManiaGame.super:new()
|
|
||||||
|
|
||||||
self.lock_drop = true
|
|
||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
self.enable_hold = true
|
self.enable_hold = true
|
||||||
|
|
||||||
self.roll_frames = 0
|
|
||||||
self.combo = 1
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomManiaGame:getARE()
|
return PhantomManiaNGame
|
||||||
if self.level < 100 then return 18
|
|
||||||
elseif self.level < 200 then return 14
|
|
||||||
elseif self.level < 400 then return 8
|
|
||||||
elseif self.level < 500 then return 7
|
|
||||||
else return 6 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getLineARE()
|
|
||||||
if self.level < 100 then return 18
|
|
||||||
elseif self.level < 400 then return 8
|
|
||||||
elseif self.level < 500 then return 7
|
|
||||||
else return 6 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getDasLimit()
|
|
||||||
if self.level < 200 then return 11
|
|
||||||
elseif self.level < 300 then return 10
|
|
||||||
elseif self.level < 400 then return 9
|
|
||||||
else return 7 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getLineClearDelay()
|
|
||||||
return self:getLineARE()
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getLockDelay()
|
|
||||||
if self.level < 100 then return 30
|
|
||||||
elseif self.level < 200 then return 26
|
|
||||||
elseif self.level < 300 then return 22
|
|
||||||
elseif self.level < 400 then return 18
|
|
||||||
else return 15 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getGravity()
|
|
||||||
return 20
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:hitTorikan(old_level, new_level)
|
|
||||||
if old_level < 300 and new_level >= 300 and self.frames > sp(2,28) then
|
|
||||||
self.level = 300
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,38) then
|
|
||||||
self.level = 500
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if old_level < 800 and new_level >= 800 and self.frames > sp(5,23) then
|
|
||||||
self.level = 800
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:advanceOneFrame()
|
|
||||||
if self.clear then
|
|
||||||
self.roll_frames = self.roll_frames + 1
|
|
||||||
if self.roll_frames < 0 then
|
|
||||||
return false
|
|
||||||
elseif self.roll_frames > 1982 then
|
|
||||||
self.completed = true
|
|
||||||
end
|
|
||||||
elseif self.ready_frames == 0 then
|
|
||||||
self.frames = self.frames + 1
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:onPieceEnter()
|
|
||||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
|
||||||
self.level = self.level + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:onLineClear(cleared_row_count)
|
|
||||||
if not self.clear then
|
|
||||||
local new_level = self.level + cleared_row_count
|
|
||||||
if self:hitTorikan(self.level, new_level) then
|
|
||||||
if new_level >= 999 then
|
|
||||||
self.level = 999
|
|
||||||
end
|
|
||||||
self.clear = true
|
|
||||||
else
|
|
||||||
self.level = new_level
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
|
|
||||||
if cleared_lines > 0 then
|
|
||||||
self.score = self.score + (
|
|
||||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
|
||||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
|
||||||
)
|
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
PhantomManiaGame.rollOpacityFunction = function(age)
|
|
||||||
if age > 4 then return 0
|
|
||||||
else return 1 - age / 4 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:drawGrid()
|
|
||||||
if not (self.game_over or self.clear) then
|
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
|
||||||
else
|
|
||||||
self.grid:draw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getLetterGrade(level, clear)
|
|
||||||
if level < 300 or level == 300 and clear then
|
|
||||||
return ""
|
|
||||||
elseif level < 500 or level == 500 and clear then
|
|
||||||
return "M"
|
|
||||||
elseif level < 700 then
|
|
||||||
return "MK"
|
|
||||||
elseif level < 800 or level == 800 and clear then
|
|
||||||
return "MV"
|
|
||||||
elseif level < 900 then
|
|
||||||
return "MO"
|
|
||||||
elseif level < 999 then
|
|
||||||
return "MM"
|
|
||||||
elseif level == 999 then
|
|
||||||
return "GM"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:drawScoringInfo()
|
|
||||||
PhantomManiaGame.super.drawScoringInfo(self)
|
|
||||||
|
|
||||||
local text_x = config["side_next"] and 320 or 240
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
|
||||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
|
||||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
|
||||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
|
||||||
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
|
|
||||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
|
||||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
|
||||||
if self.clear then
|
|
||||||
love.graphics.printf(self.level, text_x, 370, 40, "right")
|
|
||||||
else
|
|
||||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getSectionEndLevel()
|
|
||||||
if self.level > 900 then return 999
|
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getBackground()
|
|
||||||
return math.floor(self.level / 100)
|
|
||||||
end
|
|
||||||
|
|
||||||
function PhantomManiaGame:getHighscoreData()
|
|
||||||
return {
|
|
||||||
level = self.level,
|
|
||||||
frames = self.frames,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return PhantomManiaGame
|
|
||||||
|
|||||||
@@ -5,17 +5,15 @@ local Piece = require 'tetris.components.piece'
|
|||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||||
|
|
||||||
local LigneGame = GameMode:extend()
|
local Race40Game = GameMode:extend()
|
||||||
|
|
||||||
LigneGame.name = "Ligne"
|
Race40Game.name = "Race 40"
|
||||||
LigneGame.hash = "Ligne"
|
Race40Game.hash = "Race40"
|
||||||
LigneGame.tagline = "How fast can you clear 40 lines?"
|
Race40Game.tagline = "How fast can you clear 40 lines?"
|
||||||
|
|
||||||
LigneGame.arr = 1
|
|
||||||
LigneGame.drop_speed = 1
|
|
||||||
|
|
||||||
function LigneGame:new()
|
function Race40Game:new()
|
||||||
LigneGame.super:new()
|
Race40Game.super:new()
|
||||||
|
|
||||||
self.lines = 0
|
self.lines = 0
|
||||||
self.line_goal = 40
|
self.line_goal = 40
|
||||||
@@ -32,39 +30,39 @@ function LigneGame:new()
|
|||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getDropSpeed()
|
function Race40Game:getDropSpeed()
|
||||||
return 20
|
return 20
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getARR()
|
function Race40Game:getARR()
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getARE()
|
function Race40Game:getARE()
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getLineARE()
|
function Race40Game:getLineARE()
|
||||||
return self:getARE()
|
return self:getARE()
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getDasLimit()
|
function Race40Game:getDasLimit()
|
||||||
return 6
|
return 6
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getLineClearDelay()
|
function Race40Game:getLineClearDelay()
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getLockDelay()
|
function Race40Game:getLockDelay()
|
||||||
return 15
|
return 15
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getGravity()
|
function Race40Game:getGravity()
|
||||||
return 1/64
|
return 1/64
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:advanceOneFrame()
|
function Race40Game:advanceOneFrame()
|
||||||
if self.clear then
|
if self.clear then
|
||||||
self.roll_frames = self.roll_frames + 1
|
self.roll_frames = self.roll_frames + 1
|
||||||
if self.roll_frames > 150 then
|
if self.roll_frames > 150 then
|
||||||
@@ -77,11 +75,11 @@ function LigneGame:advanceOneFrame()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:onPieceLock()
|
function Race40Game:onPieceLock()
|
||||||
self.pieces = self.pieces + 1
|
self.pieces = self.pieces + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:onLineClear(cleared_row_count)
|
function Race40Game:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
self.lines = self.lines + cleared_row_count
|
self.lines = self.lines + cleared_row_count
|
||||||
if self.lines >= self.line_goal then
|
if self.lines >= self.line_goal then
|
||||||
@@ -90,22 +88,22 @@ function LigneGame:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:drawGrid(ruleset)
|
function Race40Game:drawGrid(ruleset)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
if self.piece ~= nil then
|
if self.piece ~= nil then
|
||||||
self:drawGhostPiece(ruleset)
|
self:drawGhostPiece(ruleset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getHighscoreData()
|
function Race40Game:getHighscoreData()
|
||||||
return {
|
return {
|
||||||
level = self.level,
|
level = self.level,
|
||||||
frames = self.frames,
|
frames = self.frames,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:drawScoringInfo()
|
function Race40Game:drawScoringInfo()
|
||||||
LigneGame.super.drawScoringInfo(self)
|
Race40Game.super.drawScoringInfo(self)
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
local text_x = config["side_next"] and 320 or 240
|
local text_x = config["side_next"] and 320 or 240
|
||||||
@@ -124,8 +122,8 @@ function LigneGame:drawScoringInfo()
|
|||||||
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
|
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
|
||||||
end
|
end
|
||||||
|
|
||||||
function LigneGame:getBackground()
|
function Race40Game:getBackground()
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
|
|
||||||
return LigneGame
|
return Race40Game
|
||||||
@@ -11,8 +11,8 @@ StrategyGame.name = "Strategy"
|
|||||||
StrategyGame.hash = "Strategy"
|
StrategyGame.hash = "Strategy"
|
||||||
StrategyGame.tagline = "You have lots of time to think! Can you use it to place a piece fast?"
|
StrategyGame.tagline = "You have lots of time to think! Can you use it to place a piece fast?"
|
||||||
|
|
||||||
StrategyGame.arr = 1
|
|
||||||
StrategyGame.drop_speed = 1
|
|
||||||
|
|
||||||
function StrategyGame:new()
|
function StrategyGame:new()
|
||||||
StrategyGame.super:new()
|
StrategyGame.super:new()
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ Survival2020Game.name = "Survival 2020"
|
|||||||
Survival2020Game.hash = "Survival2020"
|
Survival2020Game.hash = "Survival2020"
|
||||||
Survival2020Game.tagline = "A new time limit on the blocks! Can you handle being forced to perform under the total delay?"
|
Survival2020Game.tagline = "A new time limit on the blocks! Can you handle being forced to perform under the total delay?"
|
||||||
|
|
||||||
Survival2020Game.arr = 1
|
|
||||||
Survival2020Game.drop_speed = 1
|
|
||||||
|
|
||||||
function Survival2020Game:new()
|
function Survival2020Game:new()
|
||||||
Survival2020Game.super:new()
|
Survival2020Game.super:new()
|
||||||
@@ -36,8 +36,8 @@ function Survival2020Game:getARE()
|
|||||||
elseif self.level < 300 then return 10
|
elseif self.level < 300 then return 10
|
||||||
elseif self.level < 500 then return 6
|
elseif self.level < 500 then return 6
|
||||||
elseif self.level < 1000 then return 4
|
elseif self.level < 1000 then return 4
|
||||||
elseif self.level < 1500 then return 3
|
elseif self.level < 1500 then return 5
|
||||||
else return 2 end
|
else return 6 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Survival2020Game:getLineARE()
|
function Survival2020Game:getLineARE()
|
||||||
@@ -71,12 +71,12 @@ end
|
|||||||
|
|
||||||
function Survival2020Game:getTotalDelay()
|
function Survival2020Game:getTotalDelay()
|
||||||
if self.level < 500 then return 60
|
if self.level < 500 then return 60
|
||||||
elseif self.level < 600 then return 45 -- lock delay: 15
|
elseif self.level < 600 then return 45 -- lock delay: 13
|
||||||
elseif self.level < 700 then return 36
|
elseif self.level < 700 then return 36
|
||||||
elseif self.level < 800 then return 27
|
elseif self.level < 800 then return 27
|
||||||
elseif self.level < 900 then return 21
|
elseif self.level < 900 then return 21
|
||||||
elseif self.level < 1000 then return 15
|
elseif self.level < 1000 then return 15
|
||||||
elseif self.level < 1100 then return 36 -- lock delay: 11
|
elseif self.level < 1100 then return 36 -- lock delay: 10
|
||||||
elseif self.level < 1200 then return 27
|
elseif self.level < 1200 then return 27
|
||||||
elseif self.level < 1300 then return 21
|
elseif self.level < 1300 then return 21
|
||||||
elseif self.level < 1400 then return 15
|
elseif self.level < 1400 then return 15
|
||||||
@@ -102,15 +102,15 @@ function Survival2020Game:getNextPiece(ruleset)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Survival2020Game:hitTorikan(old_level, new_level)
|
function Survival2020Game:hitTorikan(old_level, new_level)
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,00) then
|
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,00) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,00) then
|
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(5,00) then
|
||||||
self.level = 1000
|
self.level = 1000
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 1500 and new_level >= 1500 and self.frames > sp(7,00) then
|
if old_level < 1500 and new_level >= 1500 and self.frames > frameTime(7,00) then
|
||||||
self.level = 1500
|
self.level = 1500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -193,7 +193,7 @@ function Survival2020Game:updateSectionTimes(old_level, new_level)
|
|||||||
section_time = self.frames - self.section_start_time
|
section_time = self.frames - self.section_start_time
|
||||||
table.insert(self.section_times, section_time)
|
table.insert(self.section_times, section_time)
|
||||||
self.section_start_time = self.frames
|
self.section_start_time = self.frames
|
||||||
if section_time <= sp(0,30) then
|
if section_time <= frameTime(0,30) then
|
||||||
self.grade = self.grade + 2
|
self.grade = self.grade + 2
|
||||||
else
|
else
|
||||||
self.grade = self.grade + 1
|
self.grade = self.grade + 1
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ SurvivalA1Game.name = "Survival A1"
|
|||||||
SurvivalA1Game.hash = "SurvivalA1"
|
SurvivalA1Game.hash = "SurvivalA1"
|
||||||
SurvivalA1Game.tagline = "The game starts fast and only gets faster!"
|
SurvivalA1Game.tagline = "The game starts fast and only gets faster!"
|
||||||
|
|
||||||
SurvivalA1Game.arr = 1
|
|
||||||
SurvivalA1Game.drop_speed = 1
|
|
||||||
|
|
||||||
function SurvivalA1Game:new()
|
function SurvivalA1Game:new()
|
||||||
SurvivalA1Game.super:new()
|
SurvivalA1Game.super:new()
|
||||||
@@ -25,6 +25,12 @@ function SurvivalA1Game:new()
|
|||||||
level999 = false
|
level999 = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.randomizer = History4RollsRandomizer()
|
self.randomizer = History4RollsRandomizer()
|
||||||
|
|
||||||
self.lock_drop = false
|
self.lock_drop = false
|
||||||
@@ -34,11 +40,11 @@ function SurvivalA1Game:new()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getARE()
|
function SurvivalA1Game:getARE()
|
||||||
return 25
|
return 30
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getLineARE()
|
function SurvivalA1Game:getLineARE()
|
||||||
return 25
|
return 27
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getDasLimit()
|
function SurvivalA1Game:getDasLimit()
|
||||||
@@ -46,7 +52,7 @@ function SurvivalA1Game:getDasLimit()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getLineClearDelay()
|
function SurvivalA1Game:getLineClearDelay()
|
||||||
return 40
|
return 44
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getLockDelay()
|
function SurvivalA1Game:getLockDelay()
|
||||||
@@ -125,15 +131,15 @@ end
|
|||||||
|
|
||||||
function SurvivalA1Game:checkGMRequirements(old_level, new_level)
|
function SurvivalA1Game:checkGMRequirements(old_level, new_level)
|
||||||
if old_level < 300 and new_level >= 300 then
|
if old_level < 300 and new_level >= 300 then
|
||||||
if self.score > 12000 and self.frames <= sp(4,15) then
|
if self.score > 12000 and self.frames <= frameTime(4,15) then
|
||||||
self.gm_conditions["level300"] = true
|
self.gm_conditions["level300"] = true
|
||||||
end
|
end
|
||||||
elseif old_level < 500 and new_level >= 500 then
|
elseif old_level < 500 and new_level >= 500 then
|
||||||
if self.score > 40000 and self.frames <= sp(7,30) then
|
if self.score > 40000 and self.frames <= frameTime(7,30) then
|
||||||
self.gm_conditions["level500"] = true
|
self.gm_conditions["level500"] = true
|
||||||
end
|
end
|
||||||
elseif old_level < 999 and new_level >= 999 then
|
elseif old_level < 999 and new_level >= 999 then
|
||||||
if self.score > 126000 and self.frames <= sp(13,30) then
|
if self.score > 126000 and self.frames <= frameTime(13,30) then
|
||||||
self.gm_conditions["level900"] = true
|
self.gm_conditions["level900"] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -141,9 +147,6 @@ end
|
|||||||
|
|
||||||
function SurvivalA1Game:drawGrid()
|
function SurvivalA1Game:drawGrid()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
if self.piece ~= nil and self.level < 100 then
|
|
||||||
self:drawGhostPiece(ruleset)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:drawScoringInfo()
|
function SurvivalA1Game:drawScoringInfo()
|
||||||
@@ -154,13 +157,17 @@ function SurvivalA1Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
||||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
@@ -172,13 +179,16 @@ function SurvivalA1Game:drawScoringInfo()
|
|||||||
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
||||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_8x11)
|
love.graphics.setFont(font_8x11)
|
||||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:getSectionEndLevel()
|
function SurvivalA1Game:getSectionEndLevel()
|
||||||
if self.level > 900 then return 999
|
if self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,20 @@ SurvivalA2Game.name = "Survival A2"
|
|||||||
SurvivalA2Game.hash = "SurvivalA2"
|
SurvivalA2Game.hash = "SurvivalA2"
|
||||||
SurvivalA2Game.tagline = "The game starts fast and only gets faster!"
|
SurvivalA2Game.tagline = "The game starts fast and only gets faster!"
|
||||||
|
|
||||||
SurvivalA2Game.arr = 1
|
|
||||||
SurvivalA2Game.drop_speed = 1
|
|
||||||
|
|
||||||
function SurvivalA2Game:new()
|
function SurvivalA2Game:new()
|
||||||
SurvivalA2Game.super:new()
|
SurvivalA2Game.super:new()
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_drop = true
|
||||||
end
|
end
|
||||||
@@ -46,7 +52,7 @@ function SurvivalA2Game:getDasLimit()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:getLineClearDelay()
|
function SurvivalA2Game:getLineClearDelay()
|
||||||
return self:getLineARE()
|
return self:getLineARE() - 2
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:getLockDelay()
|
function SurvivalA2Game:getLockDelay()
|
||||||
@@ -62,7 +68,7 @@ function SurvivalA2Game:getGravity()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:hitTorikan(old_level, new_level)
|
function SurvivalA2Game:hitTorikan(old_level, new_level)
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,25) then
|
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,25) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -132,23 +138,30 @@ function SurvivalA2Game:drawScoringInfo()
|
|||||||
love.graphics.print(
|
love.graphics.print(
|
||||||
self.das.direction .. " " ..
|
self.das.direction .. " " ..
|
||||||
self.das.frames .. " " ..
|
self.das.frames .. " " ..
|
||||||
st(self.prev_inputs)
|
strTrueValues(self.prev_inputs)
|
||||||
)
|
)
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||||
love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left")
|
love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left")
|
||||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:getSectionEndLevel()
|
function SurvivalA2Game:getSectionEndLevel()
|
||||||
if self.clear then return self.level
|
if self.clear then return self.level
|
||||||
elseif self.level > 900 then return 999
|
elseif self.level >= 900 then return 999
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ SurvivalA3Game.name = "Survival A3"
|
|||||||
SurvivalA3Game.hash = "SurvivalA3"
|
SurvivalA3Game.hash = "SurvivalA3"
|
||||||
SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?"
|
SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?"
|
||||||
|
|
||||||
SurvivalA3Game.arr = 1
|
|
||||||
SurvivalA3Game.drop_speed = 1
|
|
||||||
|
|
||||||
function SurvivalA3Game:new()
|
function SurvivalA3Game:new()
|
||||||
SurvivalA3Game.super:new()
|
SurvivalA3Game.super:new()
|
||||||
@@ -24,10 +24,19 @@ function SurvivalA3Game:new()
|
|||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_drop = true
|
||||||
self.enable_hold = true
|
self.enable_hold = true
|
||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
|
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getARE()
|
function SurvivalA3Game:getARE()
|
||||||
@@ -44,13 +53,14 @@ function SurvivalA3Game:getLineARE()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getDasLimit()
|
function SurvivalA3Game:getDasLimit()
|
||||||
if self.level < 200 then return 9
|
if self.level < 100 then return 9
|
||||||
elseif self.level < 500 then return 7
|
elseif self.level < 500 then return 7
|
||||||
else return 5 end
|
else return 5 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getLineClearDelay()
|
function SurvivalA3Game:getLineClearDelay()
|
||||||
return self:getLineARE() - 2
|
if self.level < 1300 then return self:getLineARE() - 2
|
||||||
|
else return 6 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getLockDelay()
|
function SurvivalA3Game:getLockDelay()
|
||||||
@@ -60,7 +70,8 @@ function SurvivalA3Game:getLockDelay()
|
|||||||
elseif self.level < 600 then return 13
|
elseif self.level < 600 then return 13
|
||||||
elseif self.level < 1100 then return 12
|
elseif self.level < 1100 then return 12
|
||||||
elseif self.level < 1200 then return 10
|
elseif self.level < 1200 then return 10
|
||||||
else return 8 end
|
elseif self.level < 1300 then return 8
|
||||||
|
else return 15 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getGravity()
|
function SurvivalA3Game:getGravity()
|
||||||
@@ -84,11 +95,11 @@ function SurvivalA3Game:getNextPiece(ruleset)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:hitTorikan(old_level, new_level)
|
function SurvivalA3Game:hitTorikan(old_level, new_level)
|
||||||
if old_level < 500 and new_level >= 500 and self.frames > sp(2,28) then
|
if old_level < 500 and new_level >= 500 and self.frames > frameTime(2,28) then
|
||||||
self.level = 500
|
self.level = 500
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(4,56) then
|
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(4,56) then
|
||||||
self.level = 1000
|
self.level = 1000
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -133,6 +144,7 @@ function SurvivalA3Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
self.clear = true
|
self.clear = true
|
||||||
self.grid:clear()
|
self.grid:clear()
|
||||||
|
self.big_mode = true
|
||||||
self.roll_frames = -150
|
self.roll_frames = -150
|
||||||
else
|
else
|
||||||
self.level = math.min(new_level, 1300)
|
self.level = math.min(new_level, 1300)
|
||||||
@@ -141,8 +153,8 @@ function SurvivalA3Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:onPieceLock()
|
function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
|
||||||
self:advanceBottomRow(1)
|
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines)
|
function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
@@ -165,8 +177,11 @@ function SurvivalA3Game:updateSectionTimes(old_level, new_level)
|
|||||||
section_time = self.frames - self.section_start_time
|
section_time = self.frames - self.section_start_time
|
||||||
table.insert(self.section_times, section_time)
|
table.insert(self.section_times, section_time)
|
||||||
self.section_start_time = self.frames
|
self.section_start_time = self.frames
|
||||||
if section_time <= sp(1,00) then
|
if section_time <= frameTime(1,00) then
|
||||||
self.grade = self.grade + 1
|
self.grade = self.grade + 1
|
||||||
|
else
|
||||||
|
self.coolregret_message = "REGRET!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -188,10 +203,8 @@ end
|
|||||||
local function getLetterGrade(grade)
|
local function getLetterGrade(grade)
|
||||||
if grade == 0 then
|
if grade == 0 then
|
||||||
return "1"
|
return "1"
|
||||||
elseif grade <= 9 then
|
|
||||||
return "S" .. tostring(grade)
|
|
||||||
else
|
else
|
||||||
return "M" .. tostring(grade - 9)
|
return "S" .. tostring(grade)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -206,6 +219,15 @@ function SurvivalA3Game:drawScoringInfo()
|
|||||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||||
|
local sg = self.grid:checkSecretGrade()
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||||
|
end
|
||||||
|
|
||||||
|
if(self.coolregret_timer > 0) then
|
||||||
|
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||||
|
self.coolregret_timer = self.coolregret_timer - 1
|
||||||
|
end
|
||||||
|
|
||||||
local current_section = math.floor(self.level / 100) + 1
|
local current_section = math.floor(self.level / 100) + 1
|
||||||
self:drawSectionTimesWithSplits(current_section)
|
self:drawSectionTimesWithSplits(current_section)
|
||||||
@@ -219,6 +241,9 @@ function SurvivalA3Game:drawScoringInfo()
|
|||||||
else
|
else
|
||||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||||
end
|
end
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getBackground()
|
function SurvivalA3Game:getBackground()
|
||||||
|
|||||||
18
tetris/randomizers/always.lua
Normal file
18
tetris/randomizers/always.lua
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local AlwaysRandomizer = Randomizer:extend()
|
||||||
|
|
||||||
|
function AlwaysRandomizer:new(piece)
|
||||||
|
self.piece = piece
|
||||||
|
self:initialize()
|
||||||
|
self.next_queue = {}
|
||||||
|
for i = 1, 30 do
|
||||||
|
table.insert(self.next_queue, self:generatePiece())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AlwaysRandomizer:generatePiece()
|
||||||
|
return self.piece
|
||||||
|
end
|
||||||
|
|
||||||
|
return AlwaysRandomizer
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
|
||||||
|
|
||||||
local AlwaysORandomizer = Randomizer:extend()
|
|
||||||
|
|
||||||
function AlwaysORandomizer:generatePiece()
|
|
||||||
return "O"
|
|
||||||
end
|
|
||||||
|
|
||||||
return AlwaysORandomizer
|
|
||||||
17
tetris/randomizers/bag5.lua
Normal file
17
tetris/randomizers/bag5.lua
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local Bag5Randomizer = Randomizer:extend()
|
||||||
|
|
||||||
|
function Bag5Randomizer:initialize()
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Bag5Randomizer:generatePiece()
|
||||||
|
if next(self.bag) == nil then
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
end
|
||||||
|
local x = math.random(table.getn(self.bag))
|
||||||
|
return table.remove(self.bag, x)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Bag5Randomizer
|
||||||
24
tetris/randomizers/bag5alt.lua
Executable file
24
tetris/randomizers/bag5alt.lua
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local Bag5AltRandomizer = Randomizer:extend()
|
||||||
|
|
||||||
|
function Bag5AltRandomizer:initialize()
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
self.prev = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function Bag5AltRandomizer:generatePiece()
|
||||||
|
if next(self.bag) == nil then
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
end
|
||||||
|
local x = math.random(table.getn(self.bag))
|
||||||
|
local temp = table.remove(self.bag, x)
|
||||||
|
if temp == self.prev then
|
||||||
|
local y = math.random(table.getn(self.bag))
|
||||||
|
temp = table.remove(self.bag, y)
|
||||||
|
end
|
||||||
|
self.prev = temp
|
||||||
|
return temp
|
||||||
|
end
|
||||||
|
|
||||||
|
return Bag5AltRandomizer
|
||||||
31
tetris/randomizers/bag7noSZOstart.lua
Normal file
31
tetris/randomizers/bag7noSZOstart.lua
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local Bag7NoSZOStartRandomizer = Randomizer:extend()
|
||||||
|
|
||||||
|
function Bag7NoSZOStartRandomizer:shuffleBag()
|
||||||
|
local b = self.bag
|
||||||
|
local ln = #b
|
||||||
|
for i = 1, ln do
|
||||||
|
local j = math.random(i, ln)
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isnotSZO(x) return not(x == "S" or x == "Z" or x == "O") end
|
||||||
|
|
||||||
|
function Bag7NoSZOStartRandomizer:initialize()
|
||||||
|
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
|
repeat
|
||||||
|
self:shuffleBag()
|
||||||
|
until isnotSZO(self.bag[7])
|
||||||
|
end
|
||||||
|
|
||||||
|
function Bag7NoSZOStartRandomizer:generatePiece()
|
||||||
|
if #self.bag == 0 then
|
||||||
|
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
|
self:shuffleBag()
|
||||||
|
end
|
||||||
|
return table.remove(self.bag)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Bag7NoSZOStartRandomizer
|
||||||
28
tetris/randomizers/bag_konoha.lua
Normal file
28
tetris/randomizers/bag_konoha.lua
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
|
local BagKonoha = Randomizer:extend()
|
||||||
|
|
||||||
|
function BagKonoha:initialize()
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
self.prev = nil
|
||||||
|
self.allowrepeat = false
|
||||||
|
self.generated = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function BagKonoha:generatePiece()
|
||||||
|
self.generated = self.generated + 1
|
||||||
|
if #self.bag == 0 then
|
||||||
|
self.bag = {"I", "J", "L", "O", "T"}
|
||||||
|
end
|
||||||
|
local x = math.random(#self.bag)
|
||||||
|
local temp = table.remove(self.bag, x)
|
||||||
|
if temp == self.prev and not self.allowrepeat then
|
||||||
|
local y = math.random(#self.bag)
|
||||||
|
table.insert(self.bag, temp) -- should insert at the end of the bag, bag[y] doesnt change
|
||||||
|
temp = table.remove(self.bag, y)
|
||||||
|
end
|
||||||
|
self.prev = temp
|
||||||
|
return temp
|
||||||
|
end
|
||||||
|
|
||||||
|
return BagKonoha
|
||||||
@@ -3,17 +3,23 @@ local Randomizer = require 'tetris.randomizers.randomizer'
|
|||||||
local History4RollsRandomizer = Randomizer:extend()
|
local History4RollsRandomizer = Randomizer:extend()
|
||||||
|
|
||||||
function History4RollsRandomizer:initialize()
|
function History4RollsRandomizer:initialize()
|
||||||
self.history = {"Z", "S", "Z", "S"}
|
self.history = {"Z", "Z", "Z", "Z"}
|
||||||
|
self.first = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function History4RollsRandomizer:generatePiece()
|
function History4RollsRandomizer:generatePiece()
|
||||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
if self.first then
|
||||||
for i = 1, 4 do
|
self.first = false
|
||||||
local x = math.random(7)
|
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||||
if not inHistory(shapes[x], self.history) or i == 4 then
|
else
|
||||||
return self:updateHistory(shapes[x])
|
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
end
|
for i = 1, 4 do
|
||||||
end
|
local x = math.random(7)
|
||||||
|
if not inHistory(shapes[x], self.history) or i == 4 then
|
||||||
|
return self:updateHistory(shapes[x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function History4RollsRandomizer:updateHistory(shape)
|
function History4RollsRandomizer:updateHistory(shape)
|
||||||
|
|||||||
@@ -4,16 +4,22 @@ local History6RollsRandomizer = Randomizer:extend()
|
|||||||
|
|
||||||
function History6RollsRandomizer:initialize()
|
function History6RollsRandomizer:initialize()
|
||||||
self.history = {"Z", "S", "Z", "S"}
|
self.history = {"Z", "S", "Z", "S"}
|
||||||
|
self.first = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:generatePiece()
|
function History6RollsRandomizer:generatePiece()
|
||||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
if self.first then
|
||||||
for i = 1, 6 do
|
self.first = false
|
||||||
local x = math.random(7)
|
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||||
if not inHistory(shapes[x], self.history) or i == 6 then
|
else
|
||||||
return self:updateHistory(shapes[x])
|
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
end
|
for i = 1, 6 do
|
||||||
end
|
local x = math.random(7)
|
||||||
|
if not inHistory(shapes[x], self.history) or i == 6 then
|
||||||
|
return self:updateHistory(shapes[x])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:updateHistory(shape)
|
function History6RollsRandomizer:updateHistory(shape)
|
||||||
|
|||||||
@@ -1,38 +1,70 @@
|
|||||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
local History6RollsRandomizer = Randomizer:extend()
|
local History6Rolls35PoolRandomizer = Randomizer:extend()
|
||||||
|
|
||||||
function History6RollsRandomizer:initialize()
|
function History6Rolls35PoolRandomizer:initialize()
|
||||||
|
self.first = true
|
||||||
self.history = {"Z", "S", "Z", "S"}
|
self.history = {"Z", "S", "Z", "S"}
|
||||||
self.bag_counts = {
|
self.pool = {
|
||||||
I = 5, J = 5, L = 5, O = 5, S = 3, T = 5, Z = 3
|
"I", "I", "I", "I", "I",
|
||||||
|
"T", "T", "T", "T", "T",
|
||||||
|
"L", "L", "L", "L", "L",
|
||||||
|
"J", "J", "J", "J", "J",
|
||||||
|
"S", "S", "S", "S", "S",
|
||||||
|
"Z", "Z", "Z", "Z", "Z",
|
||||||
|
"O", "O", "O", "O", "O",
|
||||||
}
|
}
|
||||||
|
self.droughts = {
|
||||||
|
I = 0,
|
||||||
|
T = 0,
|
||||||
|
L = 0,
|
||||||
|
J = 0,
|
||||||
|
S = 0,
|
||||||
|
Z = 0,
|
||||||
|
O = 0,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:getBagPiece(n)
|
function History6Rolls35PoolRandomizer:generatePiece()
|
||||||
for shape, count in pairs(self.bag_counts) do
|
local index, x
|
||||||
n = n - count
|
if self.first then
|
||||||
if n <= 0 then
|
local prevent = {"S", "Z", "O"}
|
||||||
return shape
|
repeat
|
||||||
end
|
index = math.random(#self.pool)
|
||||||
end
|
x = self.pool[index]
|
||||||
|
until not inHistory(x, prevent)
|
||||||
|
self.first = false
|
||||||
|
else
|
||||||
|
for i = 1, 6 do
|
||||||
|
index = math.random(#self.pool)
|
||||||
|
x = self.pool[index]
|
||||||
|
if not inHistory(x, self.history) or i == 6 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.pool[index] = self:updateHistory(x)
|
||||||
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:generatePiece()
|
function History6Rolls35PoolRandomizer:updateHistory(shape)
|
||||||
for i = 1, 6 do
|
table.remove(self.history, 1)
|
||||||
local x = self:getBagPiece(math.random(31))
|
|
||||||
if not inHistory(x, self.history) or i == 6 then
|
|
||||||
return self:updateHistory(x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function History6RollsRandomizer:updateHistory(shape)
|
|
||||||
self.bag_counts[shape] = self.bag_counts[shape] - 1
|
|
||||||
local replaced_piece = table.remove(self.history, 1)
|
|
||||||
table.insert(self.history, shape)
|
table.insert(self.history, shape)
|
||||||
self.bag_counts[replaced_piece] = self.bag_counts[replaced_piece] + 1
|
|
||||||
return shape
|
local highdrought
|
||||||
|
local highdroughtcount = 0
|
||||||
|
for k, v in pairs(self.droughts) do
|
||||||
|
if k == shape then
|
||||||
|
self.droughts[k] = 0
|
||||||
|
else
|
||||||
|
self.droughts[k] = v + 1
|
||||||
|
if v >= highdroughtcount then
|
||||||
|
highdrought = k
|
||||||
|
highdroughtcount = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return highdrought
|
||||||
end
|
end
|
||||||
|
|
||||||
function inHistory(piece, history)
|
function inHistory(piece, history)
|
||||||
@@ -44,4 +76,4 @@ function inHistory(piece, history)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return History6RollsRandomizer
|
return History6Rolls35PoolRandomizer
|
||||||
|
|||||||
@@ -4,15 +4,10 @@ local Randomizer = Object:extend()
|
|||||||
|
|
||||||
function Randomizer:new()
|
function Randomizer:new()
|
||||||
self:initialize()
|
self:initialize()
|
||||||
self.next_queue = {}
|
|
||||||
for i = 1, 30 do
|
|
||||||
table.insert(self.next_queue, self:generatePiece())
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Randomizer:nextPiece()
|
function Randomizer:nextPiece()
|
||||||
table.insert(self.next_queue, self:generatePiece())
|
return self:generatePiece()
|
||||||
return table.remove(self.next_queue, 1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Randomizer:initialize()
|
function Randomizer:initialize()
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ ARS.spawn_positions = {
|
|||||||
Z = { x=4, y=5 },
|
Z = { x=4, y=5 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=2 },
|
||||||
|
J = { x=2, y=3 },
|
||||||
|
L = { x=2, y=3 },
|
||||||
|
O = { x=2, y=3 },
|
||||||
|
S = { x=2, y=3 },
|
||||||
|
T = { x=2, y=3 },
|
||||||
|
Z = { x=2, y=3 },
|
||||||
|
}
|
||||||
|
|
||||||
ARS.block_offsets = {
|
ARS.block_offsets = {
|
||||||
I={
|
I={
|
||||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
@@ -71,11 +81,19 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
|||||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||||
) and (
|
) and (
|
||||||
piece.rotation == 0 or piece.rotation == 2
|
piece.rotation == 0 or piece.rotation == 2
|
||||||
) and (
|
) then
|
||||||
grid:isOccupied(piece.position.x, piece.position.y) or
|
local offsets = new_piece:getBlockOffsets()
|
||||||
grid:isOccupied(piece.position.x, piece.position.y - 1) or
|
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||||
grid:isOccupied(piece.position.x, piece.position.y - 2)
|
for index, offset in pairs(offsets) do
|
||||||
) then return end
|
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
|
||||||
|
|
||||||
-- kick right, kick left
|
-- kick right, kick left
|
||||||
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
|
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
|
||||||
|
|||||||
178
tetris/rulesets/arika_ace.lua
Executable file
178
tetris/rulesets/arika_ace.lua
Executable file
@@ -0,0 +1,178 @@
|
|||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||||
|
|
||||||
|
local ARS = Ruleset:extend()
|
||||||
|
|
||||||
|
ARS.name = "ACE-ARS"
|
||||||
|
ARS.hash = "ArikaACE"
|
||||||
|
|
||||||
|
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=2, y=0 },
|
||||||
|
J = { x=2, y=1 },
|
||||||
|
L = { x=2, y=1 },
|
||||||
|
O = { x=2, y=1 },
|
||||||
|
S = { x=2, y=1 },
|
||||||
|
T = { x=2, y=1 },
|
||||||
|
Z = { x=2, y=1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
ARS.block_offsets = {
|
||||||
|
I={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||||
|
},
|
||||||
|
J={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||||
|
{ {x=0, y=-1}, {x=1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
},
|
||||||
|
L={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||||
|
{ {x=0, y=-2}, {x=0, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||||
|
},
|
||||||
|
O={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
},
|
||||||
|
S={
|
||||||
|
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||||
|
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||||
|
},
|
||||||
|
T={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-1}, {x=0, y=-2} },
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=0, y=0}, {x=-1, y=-1}, {x=0, y=-2} },
|
||||||
|
},
|
||||||
|
Z={
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||||
|
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||||
|
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-- Component functions.
|
||||||
|
|
||||||
|
function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||||
|
|
||||||
|
-- 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 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) and piece.floorkick == 0 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)
|
||||||
|
piece.floorkick = 1
|
||||||
|
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
|
||||||
|
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
|
||||||
|
self:onPieceRotate(piece, grid)
|
||||||
|
piece.floorkick = 1
|
||||||
|
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})
|
||||||
|
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||||
|
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||||
|
elseif piece.shape == "T"
|
||||||
|
and new_piece.rotation == 1
|
||||||
|
and piece.floorkick == 0
|
||||||
|
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||||
|
then
|
||||||
|
-- T floorkick
|
||||||
|
piece.floorkick = piece.floorkick + 1
|
||||||
|
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARS:onPieceCreate(piece, grid)
|
||||||
|
piece.floorkick = 0
|
||||||
|
piece.manipulations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARS:onPieceDrop(piece, grid)
|
||||||
|
piece.lock_delay = 0 -- step reset
|
||||||
|
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 >= 127 then
|
||||||
|
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 >= 127 then
|
||||||
|
piece.locked = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||||
|
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||||
|
|
||||||
|
return ARS
|
||||||
177
tetris/rulesets/arika_srs.lua
Executable file
177
tetris/rulesets/arika_srs.lua
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||||
|
|
||||||
|
local SRS = Ruleset:extend()
|
||||||
|
|
||||||
|
SRS.name = "ACE-SRS"
|
||||||
|
SRS.hash = "ACE Standard"
|
||||||
|
|
||||||
|
SRS.enable_IRS_wallkicks = true
|
||||||
|
|
||||||
|
SRS.spawn_positions = {
|
||||||
|
I = { x=5, y=2 },
|
||||||
|
J = { x=4, y=3 },
|
||||||
|
L = { x=4, y=3 },
|
||||||
|
O = { x=5, y=3 },
|
||||||
|
S = { x=4, y=3 },
|
||||||
|
T = { x=4, y=3 },
|
||||||
|
Z = { x=4, y=3 },
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=0 },
|
||||||
|
J = { x=2, y=1 },
|
||||||
|
L = { x=2, y=1 },
|
||||||
|
O = { x=2, y=1 },
|
||||||
|
S = { x=2, y=1 },
|
||||||
|
T = { x=2, y=1 },
|
||||||
|
Z = { x=2, y=1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.block_offsets = {
|
||||||
|
I={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||||
|
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
|
||||||
|
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
|
||||||
|
},
|
||||||
|
J={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
|
||||||
|
},
|
||||||
|
L={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||||
|
},
|
||||||
|
O={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
},
|
||||||
|
S={
|
||||||
|
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||||
|
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||||
|
},
|
||||||
|
T={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||||
|
},
|
||||||
|
Z={
|
||||||
|
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||||
|
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.wallkicks_3x3 = {
|
||||||
|
[0]={
|
||||||
|
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||||
|
[2]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||||
|
},
|
||||||
|
[1]={
|
||||||
|
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||||
|
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||||
|
[3]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
},
|
||||||
|
[2]={
|
||||||
|
[0]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||||
|
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||||
|
},
|
||||||
|
[3]={
|
||||||
|
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||||
|
[1]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
SRS.wallkicks_line = {
|
||||||
|
[0]={
|
||||||
|
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||||
|
[2]={},
|
||||||
|
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||||
|
},
|
||||||
|
[1]={
|
||||||
|
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||||
|
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||||
|
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||||
|
},
|
||||||
|
[2]={
|
||||||
|
[0]={},
|
||||||
|
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||||
|
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||||
|
},
|
||||||
|
[3]={
|
||||||
|
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||||
|
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||||
|
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
-- Component functions.
|
||||||
|
|
||||||
|
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||||
|
|
||||||
|
local kicks
|
||||||
|
if piece.shape == "O" then
|
||||||
|
return
|
||||||
|
elseif piece.shape == "I" then
|
||||||
|
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||||
|
else
|
||||||
|
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(piece.rotation ~= new_piece.rotation)
|
||||||
|
|
||||||
|
for idx, offset in pairs(kicks) do
|
||||||
|
kicked_piece = new_piece:withOffset(offset)
|
||||||
|
if grid:canPlacePiece(kicked_piece) then
|
||||||
|
piece:setRelativeRotation(rot_dir)
|
||||||
|
piece:setOffset(offset)
|
||||||
|
self:onPieceRotate(piece, grid)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceCreate(piece, grid)
|
||||||
|
piece.manipulations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceDrop(piece, grid)
|
||||||
|
piece.lock_delay = 0 -- step reset
|
||||||
|
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 >= 127 then
|
||||||
|
piece.locked = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceRotate(piece, grid)
|
||||||
|
piece.lock_delay = 0 -- rotate reset
|
||||||
|
if piece:isDropBlocked(grid) then
|
||||||
|
piece.manipulations = piece.manipulations + 1
|
||||||
|
if piece.manipulations >= 127 then
|
||||||
|
piece.locked = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return SRS
|
||||||
@@ -16,6 +16,16 @@ ARS.spawn_positions = {
|
|||||||
Z = { x=4, y=5 },
|
Z = { x=4, y=5 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=2 },
|
||||||
|
J = { x=2, y=3 },
|
||||||
|
L = { x=2, y=3 },
|
||||||
|
O = { x=2, y=3 },
|
||||||
|
S = { x=2, y=3 },
|
||||||
|
T = { x=2, y=3 },
|
||||||
|
Z = { x=2, y=3 },
|
||||||
|
}
|
||||||
|
|
||||||
ARS.block_offsets = {
|
ARS.block_offsets = {
|
||||||
I={
|
I={
|
||||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
@@ -74,11 +84,19 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
|||||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||||
) and (
|
) and (
|
||||||
piece.rotation == 0 or piece.rotation == 2
|
piece.rotation == 0 or piece.rotation == 2
|
||||||
) and (
|
) then
|
||||||
grid:isOccupied(piece.position.x, piece.position.y) or
|
local offsets = new_piece:getBlockOffsets()
|
||||||
grid:isOccupied(piece.position.x, piece.position.y - 1) or
|
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||||
grid:isOccupied(piece.position.x, piece.position.y - 2)
|
for index, offset in pairs(offsets) do
|
||||||
) then return end
|
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
|
if piece.shape == "I" then
|
||||||
-- special kick rules for I
|
-- special kick rules for I
|
||||||
@@ -106,14 +124,21 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
|||||||
piece.floorkick = 1
|
piece.floorkick = 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif piece.shape ~= "I" then
|
else
|
||||||
-- kick right, kick left
|
-- kick right, kick left
|
||||||
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
|
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||||
elseif (grid:canPlacePiece(new_piece:withOffset({x=-1, y=0}))) then
|
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||||
end
|
elseif piece.shape == "T"
|
||||||
else
|
and new_piece.rotation == 1
|
||||||
|
and piece.floorkick == 0
|
||||||
|
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||||
|
then
|
||||||
|
-- T floorkick
|
||||||
|
piece.floorkick = piece.floorkick + 1
|
||||||
|
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ CRS.spawn_positions = {
|
|||||||
Z = { x=4, y=4 },
|
Z = { x=4, y=4 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CRS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=2 },
|
||||||
|
J = { x=2, y=3 },
|
||||||
|
L = { x=2, y=3 },
|
||||||
|
O = { x=2, y=3 },
|
||||||
|
S = { x=2, y=2 },
|
||||||
|
T = { x=2, y=3 },
|
||||||
|
Z = { x=2, y=2 },
|
||||||
|
}
|
||||||
|
|
||||||
CRS.block_offsets = {
|
CRS.block_offsets = {
|
||||||
I={
|
I={
|
||||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
|
|||||||
@@ -72,10 +72,17 @@ function Ruleset:movePiece(piece, grid, move)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Ruleset:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked, hard_drop_enabled)
|
function Ruleset:dropPiece(
|
||||||
|
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||||
|
hard_drop_enabled, additive_gravity
|
||||||
|
)
|
||||||
local y = piece.position.y
|
local y = piece.position.y
|
||||||
if inputs["down"] == true and drop_locked == false then
|
if inputs["down"] == true and drop_locked == false then
|
||||||
piece:addGravity(gravity + drop_speed, grid)
|
if additive_gravity then
|
||||||
|
piece:addGravity(gravity + drop_speed, grid)
|
||||||
|
else
|
||||||
|
piece:addGravity(math.max(gravity, drop_speed), grid)
|
||||||
|
end
|
||||||
elseif inputs["up"] == true and hard_drop_enabled == true then
|
elseif inputs["up"] == true and hard_drop_enabled == true then
|
||||||
if hard_drop_locked == true or piece:isDropBlocked(grid) then
|
if hard_drop_locked == true or piece:isDropBlocked(grid) then
|
||||||
piece:addGravity(gravity, grid)
|
piece:addGravity(gravity, grid)
|
||||||
@@ -102,12 +109,19 @@ function Ruleset:getDefaultOrientation() return 1 end
|
|||||||
function Ruleset:initializePiece(
|
function Ruleset:initializePiece(
|
||||||
inputs, data, grid, gravity, prev_inputs,
|
inputs, data, grid, gravity, prev_inputs,
|
||||||
move, lock_delay, drop_speed,
|
move, lock_delay, drop_speed,
|
||||||
drop_locked, hard_drop_locked
|
drop_locked, hard_drop_locked, big
|
||||||
)
|
)
|
||||||
|
local spawn_positions
|
||||||
|
if big then
|
||||||
|
spawn_positions = self.big_spawn_positions
|
||||||
|
else
|
||||||
|
spawn_positions = self.spawn_positions
|
||||||
|
end
|
||||||
local piece = Piece(data.shape, data.orientation - 1, {
|
local piece = Piece(data.shape, data.orientation - 1, {
|
||||||
x = self.spawn_positions[data.shape].x,
|
x = spawn_positions[data.shape].x,
|
||||||
y = self.spawn_positions[data.shape].y
|
y = spawn_positions[data.shape].y
|
||||||
}, self.block_offsets, 0, 0, data.skin)
|
}, self.block_offsets, 0, 0, data.skin, big)
|
||||||
|
|
||||||
self:onPieceCreate(piece)
|
self:onPieceCreate(piece)
|
||||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||||
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
|
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
|
||||||
@@ -120,11 +134,15 @@ function Ruleset:onPieceCreate(piece) end
|
|||||||
function Ruleset:processPiece(
|
function Ruleset:processPiece(
|
||||||
inputs, piece, grid, gravity, prev_inputs,
|
inputs, piece, grid, gravity, prev_inputs,
|
||||||
move, lock_delay, drop_speed,
|
move, lock_delay, drop_speed,
|
||||||
drop_locked, hard_drop_locked, hard_drop_enabled
|
drop_locked, hard_drop_locked,
|
||||||
|
hard_drop_enabled, additive_gravity
|
||||||
)
|
)
|
||||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false)
|
self:rotatePiece(inputs, piece, grid, prev_inputs, false)
|
||||||
self:movePiece(piece, grid, move)
|
self:movePiece(piece, grid, move)
|
||||||
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked, hard_drop_enabled)
|
self:dropPiece(
|
||||||
|
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||||
|
hard_drop_enabled, additive_gravity
|
||||||
|
)
|
||||||
self:lockPiece(piece, grid, lock_delay)
|
self:lockPiece(piece, grid, lock_delay)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
26
tetris/rulesets/standard_exp.lua
Normal file → Executable file
26
tetris/rulesets/standard_exp.lua
Normal file → Executable file
@@ -3,19 +3,29 @@ local Ruleset = require 'tetris.rulesets.ruleset'
|
|||||||
|
|
||||||
local SRS = Ruleset:extend()
|
local SRS = Ruleset:extend()
|
||||||
|
|
||||||
SRS.name = "SRS"
|
SRS.name = "Guideline SRS"
|
||||||
SRS.hash = "Standard"
|
SRS.hash = "Standard"
|
||||||
|
|
||||||
SRS.enable_IRS_wallkicks = true
|
SRS.enable_IRS_wallkicks = true
|
||||||
|
|
||||||
SRS.spawn_positions = {
|
SRS.spawn_positions = {
|
||||||
I = { x=5, y=4 },
|
I = { x=5, y=2 },
|
||||||
J = { x=4, y=5 },
|
J = { x=4, y=3 },
|
||||||
L = { x=4, y=5 },
|
L = { x=4, y=3 },
|
||||||
O = { x=5, y=5 },
|
O = { x=5, y=3 },
|
||||||
S = { x=4, y=5 },
|
S = { x=4, y=3 },
|
||||||
T = { x=4, y=5 },
|
T = { x=4, y=3 },
|
||||||
Z = { x=4, y=5 },
|
Z = { x=4, y=3 },
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=0 },
|
||||||
|
J = { x=2, y=1 },
|
||||||
|
L = { x=2, y=1 },
|
||||||
|
O = { x=2, y=1 },
|
||||||
|
S = { x=2, y=1 },
|
||||||
|
T = { x=2, y=1 },
|
||||||
|
Z = { x=2, y=1 },
|
||||||
}
|
}
|
||||||
|
|
||||||
SRS.block_offsets = {
|
SRS.block_offsets = {
|
||||||
|
|||||||
177
tetris/rulesets/ti_srs.lua
Normal file
177
tetris/rulesets/ti_srs.lua
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
local Piece = require 'tetris.components.piece'
|
||||||
|
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||||
|
|
||||||
|
local SRS = Ruleset:extend()
|
||||||
|
|
||||||
|
SRS.name = "Ti-World"
|
||||||
|
SRS.hash = "Bad I-kicks"
|
||||||
|
|
||||||
|
SRS.enable_IRS_wallkicks = true
|
||||||
|
|
||||||
|
SRS.spawn_positions = {
|
||||||
|
I = { x=5, y=2 },
|
||||||
|
J = { x=4, y=3 },
|
||||||
|
L = { x=4, y=3 },
|
||||||
|
O = { x=5, y=3 },
|
||||||
|
S = { x=4, y=3 },
|
||||||
|
T = { x=4, y=3 },
|
||||||
|
Z = { x=4, y=3 },
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.big_spawn_positions = {
|
||||||
|
I = { x=2, y=0 },
|
||||||
|
J = { x=2, y=1 },
|
||||||
|
L = { x=2, y=1 },
|
||||||
|
O = { x=2, y=1 },
|
||||||
|
S = { x=2, y=1 },
|
||||||
|
T = { x=2, y=1 },
|
||||||
|
Z = { x=2, y=1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.block_offsets = {
|
||||||
|
I={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||||
|
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
|
||||||
|
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
|
||||||
|
},
|
||||||
|
J={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
|
||||||
|
},
|
||||||
|
L={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||||
|
},
|
||||||
|
O={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||||
|
},
|
||||||
|
S={
|
||||||
|
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||||
|
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||||
|
},
|
||||||
|
T={
|
||||||
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
||||||
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
||||||
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||||
|
},
|
||||||
|
Z={
|
||||||
|
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
|
||||||
|
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||||
|
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||||
|
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRS.wallkicks_3x3 = {
|
||||||
|
[0]={
|
||||||
|
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||||
|
[2]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||||
|
},
|
||||||
|
[1]={
|
||||||
|
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||||
|
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||||
|
[3]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
},
|
||||||
|
[2]={
|
||||||
|
[0]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||||
|
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||||
|
},
|
||||||
|
[3]={
|
||||||
|
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||||
|
[1]={{x=0, y=1}, {x=0, y=-1}},
|
||||||
|
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
SRS.wallkicks_line = {
|
||||||
|
[0]={
|
||||||
|
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||||
|
[2]={},
|
||||||
|
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||||
|
},
|
||||||
|
[1]={
|
||||||
|
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||||
|
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||||
|
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||||
|
},
|
||||||
|
[2]={
|
||||||
|
[0]={},
|
||||||
|
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||||
|
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||||
|
},
|
||||||
|
[3]={
|
||||||
|
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||||
|
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||||
|
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
-- Component functions.
|
||||||
|
|
||||||
|
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||||
|
|
||||||
|
local kicks
|
||||||
|
if piece.shape == "O" then
|
||||||
|
return
|
||||||
|
elseif piece.shape == "I" then
|
||||||
|
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||||
|
else
|
||||||
|
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(piece.rotation ~= new_piece.rotation)
|
||||||
|
|
||||||
|
for idx, offset in pairs(kicks) do
|
||||||
|
kicked_piece = new_piece:withOffset(offset)
|
||||||
|
if grid:canPlacePiece(kicked_piece) then
|
||||||
|
piece:setRelativeRotation(rot_dir)
|
||||||
|
piece:setOffset(offset)
|
||||||
|
self:onPieceRotate(piece, grid)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceCreate(piece, grid)
|
||||||
|
piece.manipulations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceDrop(piece, grid)
|
||||||
|
piece.lock_delay = 0 -- step reset
|
||||||
|
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 >= 8 then
|
||||||
|
piece.locked = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SRS:onPieceRotate(piece, grid)
|
||||||
|
piece.lock_delay = 0 -- rotate reset
|
||||||
|
if piece:isDropBlocked(grid) then
|
||||||
|
piece.manipulations = piece.manipulations + 1
|
||||||
|
if piece.manipulations >= 8 then
|
||||||
|
piece.locked = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return SRS
|
||||||
Reference in New Issue
Block a user