Compare commits

...

20 Commits
v0.1 ... v0.1.2

Author SHA1 Message Date
Joe Zeng
c4ba80b60d Fixed a roll level bug in Phantom Mania. (#18)
Also refactored Phantom Mania N so that any changes made to regular Phantom Mania get applied automatically.
2019-06-09 09:14:43 -04:00
Joe Zeng
2ba957f65a Updated the README with instructions for running the trunk release. 2019-06-04 11:44:39 -04:00
Joe Zeng
321de8564c Background 20 doesn't exist!
Should _actually_ fix #1 this time.
2019-06-03 23:16:24 -04:00
Joe Zeng
96ac054cf6 Stopped bottom-row garbage from clearing 5 lines. (#16)
Resolves #15.

1) Cleared row count is marked before the onPieceLock method is called, letting the piece lock procedure react to the count of rows the piece is about to clear. (In practice, only 0 and non-0 will be different.)

2) The modes with bottom-row garbage will not advance the garbage counter when the piece is about to clear lines, as should be the case.

Also included:

3) Changed the Always O Randomizer to the Always Randomizer that takes which piece it should "always" produce as an argument in the constructor.

4) Fixed the torikan for level 800 in Phantom Mania 2. It should have been 4:45, not 4:40.
2019-06-03 23:12:48 -04:00
Joe Zeng
b7fc51f4bd Made the coding convention examples a little more expressive. 2019-06-01 23:41:36 -04:00
Joe Z
04ccd628be Fixed up the docs. 2019-05-31 23:59:04 -04:00
Joe Z
704e6dae55 Fixed the pacer test mode's backgrounds and strikes. 2019-05-31 23:25:27 -04:00
Joe Zeng
19d1686b9d Added an example of a "multiline if block". 2019-05-30 17:09:12 -04:00
Joe Zeng
aa01479c12 It's 2019. 2019-05-30 11:32:38 -04:00
Joe Z
5a1b137c2a Made it so that level 900 no longer displays 1000 on the bottom where it should display 999. 2019-05-29 22:27:50 -04:00
Joe Z
956e826bb2 Added the ghost piece to modes that needed them. 2019-05-29 22:10:27 -04:00
Joe Z
cebe57dd1e Removed some of the extraneous static properties.
ARR and drop speed are handled by getters now.
2019-05-29 21:59:53 -04:00
smiegrin
1db0a0ef05 Allow the window to be resized freely. (#8)
TODO: add option in config (#5) to set window size, and then save the current window size when resized so the next time you open the app, it opens at the same size you closed it with.
2019-05-29 21:55:01 -04:00
Joe Z
0e7a2bb9fe Turns out I corrected some of the delays too far the other way. 2019-05-29 21:50:14 -04:00
smiegrin
31b0f60475 Corrects survival_a1.lua crash. (#7)
Due to a stupid copy-paste error.
2019-05-29 19:47:51 -04:00
Joe Z
1d6ac62d7d Added drop bonuses and fixed some of the delays. 2019-05-29 00:52:54 -04:00
Joe Zeng
b316d9617b Updated Marathon A1's combo calculation algorithm.
Apparently the 1/6/15/28 multiplier comes from updating combo before calculating the multiplier, not something that gets independently multiplied.
2019-05-28 21:26:38 -04:00
Joe Z
99f7ff72fd Fixed the credit roll in Marathon 2020. 2019-05-26 16:34:35 -04:00
Joe Z
df489a2603 So were the rulesets. 2019-05-26 11:07:28 -04:00
Joe Zeng
4105040334 Game modes are kinda out of date.
Here's to clean them up before I make the complete list.
2019-05-23 13:21:26 -04:00
44 changed files with 279 additions and 688 deletions

View File

@@ -3,10 +3,44 @@ Coding conventions
* Use tabs to indent, spaces to align. * 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. * 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. * 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:
* Comments with lines at the end of code must be one line long. Multi-line comments must appeare in their own block.
```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 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 = math.min(self.drop_bonus - 1, 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. * 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
-- member variables are also variables, this should be "bleep_bloop_subscore"
end
```
Contributor's License Agreement Contributor's License Agreement
------------------------------- -------------------------------
@@ -15,4 +49,4 @@ 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.)

View File

@@ -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
@@ -19,11 +17,3 @@ 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.

56
README.md Normal file
View File

@@ -0,0 +1,56 @@
Cambridge
=========
Welcome to Cambridge, the next open-source falling-block game engine!
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 [https://brew.sh/](Homebrew), 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/joezeng/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.

BIN
dist/win32/OpenAL32.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/SDL2.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/love.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/lua51.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/mpg123.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/msvcp120.dll vendored Normal file

Binary file not shown.

BIN
dist/win32/msvcr120.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/OpenAL32.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/SDL2.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/love.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/lua51.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/mpg123.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/msvcp120.dll vendored Normal file

Binary file not shown.

BIN
dist/windows/msvcr120.dll vendored Normal file

Binary file not shown.

View File

@@ -9,35 +9,42 @@ 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, to ultimately achieve the title of Grand Master.
* **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 N1**: NES Tetris A-type.
SURVIVAL 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.
RACE PHANTOM MANIA
---- -------------
Modes in which the goal is to achieve a fixed goal in the shortest time. Modes where pieces turn invisible as soon as you lock them. One of Cambridge's signature features.
* **RACE 40L**: Clear 40 lines as fast as possible. * **Phantom Mania**: The classic invisible mode from Nullpomino. Can you handle 999 levels of "the M roll"?
* **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! * **Phantom Mania 2**: Phantom Mania but way faster! Can you face a mode where even the garbage and the next preview turn invisible?
* **RACE 10K**: Clear 10,000 lines as fast as possible. Don't worry about topping out!
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?

View File

@@ -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]

View File

@@ -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.

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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,7 +64,7 @@ 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()
@@ -72,7 +75,7 @@ function love.draw()
(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

View File

@@ -8,10 +8,10 @@ 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',

View File

@@ -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()

View File

@@ -114,42 +114,49 @@ 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
) )
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)
if self.piece:isDropBlocked(self.grid) and
not self.drop_locked and not self.drop_locked and
self.instant_soft_drop self.instant_soft_drop
then then
self.piece.locked = true 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 +191,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
@@ -374,7 +389,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) st(self.prev_inputs) ..
self.drop_bonus
) )
love.graphics.setFont(font_8x11) love.graphics.setFont(font_8x11)

View File

@@ -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()
@@ -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

View File

@@ -11,8 +11,8 @@ LigneGame.name = "Ligne"
LigneGame.hash = "Ligne" LigneGame.hash = "Ligne"
LigneGame.tagline = "How fast can you clear 40 lines?" LigneGame.tagline = "How fast can you clear 40 lines?"
LigneGame.arr = 1
LigneGame.drop_speed = 1
function LigneGame:new() function LigneGame:new()
LigneGame.super:new() LigneGame.super:new()

View File

@@ -165,7 +165,9 @@ function Marathon2020Game:onPieceEnter()
end end
function Marathon2020Game:whilePieceActive() function Marathon2020Game:whilePieceActive()
if not self.clear then
self.grade_point_decay_counter = self.grade_point_decay_counter + self.grade + 2 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)
@@ -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()

View File

@@ -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()
@@ -34,11 +33,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 +45,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 +141,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
@@ -172,6 +171,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()
@@ -206,7 +208,7 @@ function MarathonA1Game:drawScoringInfo()
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

View File

@@ -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()
@@ -35,16 +35,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()
@@ -352,7 +352,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

View File

@@ -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()
@@ -346,6 +346,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
@@ -420,7 +423,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

View File

@@ -11,8 +11,8 @@ MarathonL1Game.name = "Line Attack"
MarathonL1Game.hash = "MarathonL1" MarathonL1Game.hash = "MarathonL1"
MarathonL1Game.tagline = "Can you clear the time hurdles when the game goes this fast?" MarathonL1Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
MarathonL1Game.arr = 1
MarathonL1Game.drop_speed = 1
function MarathonL1Game:new() function MarathonL1Game:new()
MarathonL1Game.super:new() MarathonL1Game.super:new()

View File

@@ -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

View File

@@ -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()
@@ -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

View File

@@ -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()
@@ -92,7 +92,7 @@ function PhantomMania2Game:hitTorikan(old_level, new_level)
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 > sp(4,45) then
self.level = 800 self.level = 800
return true return true
end end
@@ -155,8 +155,8 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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()
@@ -34,11 +34,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 +46,7 @@ function SurvivalA1Game:getDasLimit()
end end
function SurvivalA1Game:getLineClearDelay() function SurvivalA1Game:getLineClearDelay()
return 40 return 44
end end
function SurvivalA1Game:getLockDelay() function SurvivalA1Game:getLockDelay()
@@ -141,9 +141,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()
@@ -178,7 +175,7 @@ function SurvivalA1Game:drawScoringInfo()
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

View File

@@ -11,8 +11,8 @@ 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()
@@ -46,7 +46,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()
@@ -148,7 +148,7 @@ 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

View File

@@ -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()
@@ -141,8 +141,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)

View 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

View File

@@ -1,9 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local AlwaysORandomizer = Randomizer:extend()
function AlwaysORandomizer:generatePiece()
return "O"
end
return AlwaysORandomizer