Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f31beffab8 | ||
|
|
2ff8fb5edc | ||
|
|
1bf8f91ef2 | ||
|
|
ba5f78d5f1 | ||
|
|
f7c4908062 | ||
|
|
3aa5bae7be | ||
|
|
40a2e78280 | ||
|
|
696da3fa3f | ||
|
|
4afe9f2bd4 | ||
|
|
1f686fb5d4 | ||
|
|
f4779c9847 | ||
|
|
06cbec4bc8 | ||
|
|
668564ffb0 | ||
|
|
e6edeea3d1 | ||
|
|
513cd6ba90 | ||
|
|
1beef8f157 | ||
|
|
d3b2b4c2d9 | ||
|
|
2b8b9d5084 | ||
|
|
2728780c45 | ||
|
|
ca592a3bcf | ||
|
|
b6f4158d70 | ||
|
|
e43f5c470a | ||
|
|
7bcdc517c0 | ||
|
|
1d30987f9a | ||
|
|
1dd46a11ef | ||
|
|
935c7aa14c | ||
|
|
aea115d953 | ||
|
|
3d5b33f41a | ||
|
|
29f07bb6ab | ||
|
|
891f96e814 | ||
|
|
36837a3af5 | ||
|
|
01b0f9f618 | ||
|
|
7c8c5bb11d | ||
|
|
acaa6bdbbf | ||
|
|
c37757f592 | ||
|
|
905e4bcc77 | ||
|
|
d956647678 | ||
|
|
10f032b49b | ||
|
|
5590e6c89b | ||
|
|
0393396d74 | ||
|
|
8c1eaec1aa | ||
|
|
957802a78e | ||
|
|
169a4e4d2f | ||
|
|
48aee18340 | ||
|
|
7b496d9412 | ||
|
|
7abb861446 | ||
|
|
21f8769228 | ||
|
|
44423fd2e8 | ||
|
|
351fb4cfe9 | ||
|
|
103f04ceaa | ||
|
|
88d2f0d8d1 | ||
|
|
e100289c82 | ||
|
|
e38da49180 | ||
|
|
b03473d2fe | ||
|
|
cf6e0be4e7 | ||
|
|
2bc9dc179c | ||
|
|
d626926d5a | ||
|
|
721acefea0 | ||
|
|
b9b71e90bb | ||
|
|
9f61b139fd | ||
|
|
3b0fdba27d | ||
|
|
d8fad3dc37 | ||
|
|
6d326a142c | ||
|
|
b6f1072587 | ||
|
|
eef04ebf05 | ||
|
|
e24737a3b8 | ||
|
|
f9368fa806 | ||
|
|
189feb1802 | ||
|
|
dc09dabacb | ||
|
|
e13278c6a8 | ||
|
|
f7f11b0e22 | ||
|
|
869a0f7ec5 | ||
|
|
10a9d97848 | ||
|
|
a470b40def | ||
|
|
fd739dcfdf | ||
|
|
e1dc01d0d0 | ||
|
|
7228707241 | ||
|
|
af86ce3a98 | ||
|
|
78ae0ae671 | ||
|
|
c614e9c4cd | ||
|
|
b27ef0e9f4 | ||
|
|
843b1e108a | ||
|
|
a8d697064c | ||
|
|
cf32474898 | ||
|
|
6a295cad59 | ||
|
|
2d80e20c82 | ||
|
|
2279c24d11 | ||
|
|
8510ad9bea | ||
|
|
6b77ad8547 | ||
|
|
6834e92674 | ||
|
|
3479374686 | ||
|
|
863c614a4c | ||
|
|
49e52c6a39 | ||
|
|
a105086ca6 | ||
|
|
1b381c4bf3 | ||
|
|
91a87fea73 | ||
|
|
28b455fcc0 | ||
|
|
2e3eff025f | ||
|
|
4670cb7c15 | ||
|
|
9b04e14388 | ||
|
|
f52da36bf7 | ||
|
|
76142c1dff | ||
|
|
a3458e2413 | ||
|
|
7eba9c012f | ||
|
|
4d2868b7b6 | ||
|
|
2e6fcd232b | ||
|
|
10833f2ec1 | ||
|
|
abb2b9491e | ||
|
|
062ab2005e | ||
|
|
468025fc80 | ||
|
|
c8544975d6 | ||
|
|
6776229bfb | ||
|
|
84b4dc5073 | ||
|
|
35dafb6615 | ||
|
|
3641d85fcb | ||
|
|
9b89c4d1de | ||
|
|
2dba120919 | ||
|
|
9224f271b1 | ||
|
|
febb5d546c | ||
|
|
c6482c423e | ||
|
|
6beb313c6b | ||
|
|
eb70f55b6e | ||
|
|
0badcde9ad | ||
|
|
6f39b591d3 | ||
|
|
129237f0b0 | ||
|
|
741c246244 | ||
|
|
b5937af8b2 | ||
|
|
33b8533d8e | ||
|
|
69959ff687 | ||
|
|
f91cd99dfd | ||
|
|
be59727ca5 | ||
|
|
cca295066c | ||
|
|
f2862b4d93 | ||
|
|
2aafd30253 | ||
|
|
b27ba335ba | ||
|
|
33244736b8 | ||
|
|
a324e0015a | ||
|
|
285108ca08 | ||
|
|
4b1fed727c | ||
|
|
d38168ca00 | ||
|
|
b0ce0f17f5 | ||
|
|
9fca272e8d | ||
|
|
5a21c8244b | ||
|
|
4923b2e2d4 | ||
|
|
8810f24e7a | ||
|
|
57a9f6ef55 | ||
|
|
342036bc28 | ||
|
|
78dcfe43c4 | ||
|
|
cdf6b5cf33 | ||
|
|
e6a60b0021 | ||
|
|
5f29c987f2 | ||
|
|
608d75b1ac | ||
|
|
1427c0d19e | ||
|
|
e221a91d73 | ||
|
|
bdcd25b82c | ||
|
|
a5158e0994 | ||
|
|
d946b17e13 | ||
|
|
69a5c0a21a | ||
|
|
b6423c3335 | ||
|
|
5b960d7291 | ||
|
|
54f4b0b890 | ||
|
|
8c62f321a0 | ||
|
|
fdffd2cd9a | ||
|
|
8ddf468121 | ||
|
|
8e77407ff2 | ||
|
|
92c852d178 | ||
|
|
f658ed63f2 | ||
|
|
c2d1c1183c | ||
|
|
36c568feaf | ||
|
|
bf30fcefbd | ||
|
|
d9f5bd16d7 | ||
|
|
d978ff8d87 | ||
|
|
b47d0f36b9 | ||
|
|
abc210c69c | ||
|
|
436e4ac861 | ||
|
|
a48d7c67b5 | ||
|
|
f6ca79ff91 | ||
|
|
4eb3901610 | ||
|
|
3f7fc4b622 | ||
|
|
ac7ae91c39 | ||
|
|
0c8e910245 | ||
|
|
6233ffb12d | ||
|
|
1f78bb9e99 | ||
|
|
a125c09106 | ||
|
|
090ffa5126 | ||
|
|
12a6f42198 | ||
|
|
0c317d9ce1 | ||
|
|
eddfee566d | ||
|
|
7fe366a8de | ||
|
|
55be30c99f | ||
|
|
36ceef8488 | ||
|
|
b59edb5e8e | ||
|
|
5d32b6a3e7 | ||
|
|
05230ac046 | ||
|
|
f28dc08ae2 | ||
|
|
ecd958bdc5 | ||
|
|
43f59cfde8 | ||
|
|
00c46961f9 | ||
|
|
0a0053276b | ||
|
|
d0f1d869a8 | ||
|
|
29ee000998 | ||
|
|
995fd7fee9 | ||
|
|
67abf35a28 | ||
|
|
9982613e26 | ||
|
|
629beb7240 | ||
|
|
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 |
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
|||||||
*.love
|
*.love
|
||||||
dist/*.zip
|
dist/*.zip
|
||||||
dist/**/cambridge.exe
|
dist/**/cambridge.exe
|
||||||
|
dist/**/libs
|
||||||
|
dist/**/*.md
|
||||||
66
README.md
@@ -1,33 +1,66 @@
|
|||||||
|

|
||||||
|
|
||||||
|
Important notice
|
||||||
|
================
|
||||||
|
|
||||||
|
Tetra Online was recently struck by an illegal DMCA claim from the Tetris Company.
|
||||||
|
Please, spread awareness of this, and use the hashtag #DMCAgaming in your posts.
|
||||||
|
|
||||||
Cambridge
|
Cambridge
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Welcome to Cambridge, the next open-source falling-block game engine!
|
Welcome to Cambridge, the next open-source falling-block game engine!
|
||||||
|
|
||||||
|
This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)!
|
||||||
|
|
||||||
Installation instructions
|
Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Pre-built releases are available on the releases page.
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for being my co-dev!
|
||||||
|
- [joezeng](https://github.com/joezeng) for the original project, and for offering to help with the expansion!
|
||||||
|
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||||
|
- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
|
||||||
|
|
||||||
|
The following people in no particular order also helped with the project:
|
||||||
|
- [Hailey](https://github.com/haileylgbt)
|
||||||
|
- CylinderKnot
|
||||||
|
- MarkGamed7794
|
||||||
|
- [Mizu](https://github.com/rexxt)
|
||||||
|
- MattMayuga
|
||||||
|
- Kitaru
|
||||||
|
- switchpalacecorner
|
||||||
|
- [sinefuse](https://github.com/sinefuse)
|
||||||
|
- [2Tie](https://github.com/2Tie)
|
||||||
|
- [nightmareci](https://github.com/nightmareci)
|
||||||
|
- [MyPasswordIsWeak](https://github.com/MyPasswordIsWeak)
|
||||||
|
- [Dr Ocelot](https://github.com/Dr-Ocelot)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Playing the game
|
||||||
|
----------------
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Unzip the exe file and run it directly. All assets are currently bundled inside the executable.
|
You do not need LÖVE on Windows, as it comes bundled with the program.
|
||||||
|
|
||||||
### macOS
|
To get the stable release, simply download the ZIP in the latest release. All assets needed are bundled with the executable.
|
||||||
|
|
||||||
For the time being, the file `cambridge.love` only works on the command line. Install `love` with [https://brew.sh/](Homebrew), and run:
|
If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
|
||||||
|
|
||||||
$ love cambridge.love
|
Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
|
||||||
|
|
||||||
### Linux
|
dist\windows\love.exe .
|
||||||
|
|
||||||
Same as macOS, except install `love` with your favourite package manager.
|
Alternatively, if you're on a 32-bit system, run this instead:
|
||||||
|
|
||||||
|
dist\win32\love.exe .
|
||||||
|
|
||||||
Running from source
|
32-bit systems do not support rich presence integration.
|
||||||
-------------------
|
|
||||||
|
|
||||||
If you want the bleeding-edge release, you can also clone the code straight from this repository.
|
Then, check the mod pack section at the bottom of this page.
|
||||||
|
|
||||||
### macOS, Linux
|
### macOS, Linux
|
||||||
|
|
||||||
@@ -35,7 +68,9 @@ If you haven't already, install `love` with your favourite package manager (Home
|
|||||||
|
|
||||||
Clone the repository in git:
|
Clone the repository in git:
|
||||||
|
|
||||||
git clone https://github.com/joezeng/cambridge
|
git clone https://github.com/SashLilac/cambridge
|
||||||
|
|
||||||
|
Alternatively, download the source code ZIP in the latest release.
|
||||||
|
|
||||||
Then, navigate to the root directory that you just cloned, and type:
|
Then, navigate to the root directory that you just cloned, and type:
|
||||||
|
|
||||||
@@ -43,6 +78,11 @@ Then, navigate to the root directory that you just cloned, and type:
|
|||||||
|
|
||||||
It should run automatically!
|
It should run automatically!
|
||||||
|
|
||||||
|
## Installing modpacks
|
||||||
|
|
||||||
|
Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically.
|
||||||
|
|
||||||
|
For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|||||||
10
SOURCES.md
@@ -8,7 +8,7 @@ Some of the assets are used without proper licenses. We aim to have fully licens
|
|||||||
Backgrounds
|
Backgrounds
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
1. Title: "Motus Glacies." Contributed by Daniel "Explo" McCarthy.
|
1. Title: Original picrute found on the Wikipedia article for Cambridge
|
||||||
|
|
||||||
1. *Gameplay level 0: "Quantum foam." Alex Sukontsev. https://www.flickr.com/photos/control9/14957509814/
|
1. *Gameplay level 0: "Quantum foam." Alex Sukontsev. https://www.flickr.com/photos/control9/14957509814/
|
||||||
2. *Gameplay level 1: No name. http://www.onekind.tv/univision-mqb/q5mqh5brlvuuj2nhdx7ch7eum183uu
|
2. *Gameplay level 1: No name. http://www.onekind.tv/univision-mqb/q5mqh5brlvuuj2nhdx7ch7eum183uu
|
||||||
@@ -34,6 +34,14 @@ Backgrounds
|
|||||||
Backgrounds marked with a * are placeholders that will be replaced in later versions due to incompatible licenses. We are generally aiming for public domain background images, but will also accept backgrounds given proper licenses to be included within Cambridge.
|
Backgrounds marked with a * are placeholders that will be replaced in later versions due to incompatible licenses. We are generally aiming for public domain background images, but will also accept backgrounds given proper licenses to be included within Cambridge.
|
||||||
|
|
||||||
|
|
||||||
|
Sounds
|
||||||
|
------
|
||||||
|
|
||||||
|
All piece sounds are (c) 2020 Damian Yerrick.
|
||||||
|
Other sounds from:
|
||||||
|
- NullpoMino
|
||||||
|
- DTET, (c) 2003 Mihys.
|
||||||
|
|
||||||
Music
|
Music
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|||||||
11
clean.bat
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@del cambridge.love
|
||||||
|
@del dist\windows\cambridge.exe
|
||||||
|
@del dist\windows\SOURCES.md
|
||||||
|
@del dist\windows\LICENSE.md
|
||||||
|
@rmdir /Q /S dist\windows\libs
|
||||||
|
@del dist\win32\cambridge.exe
|
||||||
|
@del dist\win32\SOURCES.md
|
||||||
|
@del dist\win32\LICENSE.md
|
||||||
|
@rmdir /Q /S dist\win32\libs
|
||||||
|
@del dist\cambridge-windows.zip
|
||||||
|
@del dist\cambridge-win32.zip
|
||||||
32
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,17 +44,26 @@ 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
|
||||||
return string.sub(s, 1, pos)
|
return string.sub(s, 1, pos)
|
||||||
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
|
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Mod1(n, m)
|
||||||
|
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
|
||||||
|
return ((n-1) % m) + 1
|
||||||
|
end
|
||||||
BIN
libs/discord-rpc.dll
Normal file
BIN
libs/discord-rpc.dylib
Normal file
BIN
libs/discord-rpc.so
Normal file
282
libs/discordRPC.lua
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
local ffi = require "ffi"
|
||||||
|
|
||||||
|
|
||||||
|
-- Get the host os to load correct lib
|
||||||
|
local osname = love.system.getOS()
|
||||||
|
local discordRPClib = nil
|
||||||
|
|
||||||
|
-- FFI requires the libraries really be files just sitting in the filesystem. It
|
||||||
|
-- can't load libraries from a .love archive, nor a fused executable on Windows.
|
||||||
|
-- Merely using love.filesystem.getSource() only works when running LOVE with
|
||||||
|
-- the game unarchived from command line, like "love .".
|
||||||
|
--
|
||||||
|
-- The code here setting "source" will set the directory where the game was run
|
||||||
|
-- from, so FFI can load discordRPC. We assume that the discordRPC library's
|
||||||
|
-- libs directory is in the same directory as the .love archive; if it's
|
||||||
|
-- missing, it just won't load.
|
||||||
|
local source = love.filesystem.getSource()
|
||||||
|
if string.sub(source, -5) == ".love" or love.filesystem.isFused() then
|
||||||
|
source = love.filesystem.getSourceBaseDirectory()
|
||||||
|
end
|
||||||
|
|
||||||
|
if osname == "Linux" then
|
||||||
|
discordRPClib = ffi.load(source.."/libs/discord-rpc.so")
|
||||||
|
elseif osname == "OS X" then
|
||||||
|
discordRPClib = ffi.load(source.."/libs/discord-rpc.dylib")
|
||||||
|
elseif osname == "Windows" then
|
||||||
|
discordRPClib = ffi.load(source.."/libs/discord-rpc.dll")
|
||||||
|
else
|
||||||
|
-- Else it crashes later on
|
||||||
|
error(string.format("Discord rpc not supported on platform (%s)", osname))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
ffi.cdef[[
|
||||||
|
typedef struct DiscordRichPresence {
|
||||||
|
const char* state; /* max 128 bytes */
|
||||||
|
const char* details; /* max 128 bytes */
|
||||||
|
int64_t startTimestamp;
|
||||||
|
int64_t endTimestamp;
|
||||||
|
const char* largeImageKey; /* max 32 bytes */
|
||||||
|
const char* largeImageText; /* max 128 bytes */
|
||||||
|
const char* smallImageKey; /* max 32 bytes */
|
||||||
|
const char* smallImageText; /* max 128 bytes */
|
||||||
|
const char* partyId; /* max 128 bytes */
|
||||||
|
int partySize;
|
||||||
|
int partyMax;
|
||||||
|
const char* matchSecret; /* max 128 bytes */
|
||||||
|
const char* joinSecret; /* max 128 bytes */
|
||||||
|
const char* spectateSecret; /* max 128 bytes */
|
||||||
|
int8_t instance;
|
||||||
|
} DiscordRichPresence;
|
||||||
|
|
||||||
|
typedef struct DiscordUser {
|
||||||
|
const char* userId;
|
||||||
|
const char* username;
|
||||||
|
const char* discriminator;
|
||||||
|
const char* avatar;
|
||||||
|
} DiscordUser;
|
||||||
|
|
||||||
|
typedef void (*readyPtr)(const DiscordUser* request);
|
||||||
|
typedef void (*disconnectedPtr)(int errorCode, const char* message);
|
||||||
|
typedef void (*erroredPtr)(int errorCode, const char* message);
|
||||||
|
typedef void (*joinGamePtr)(const char* joinSecret);
|
||||||
|
typedef void (*spectateGamePtr)(const char* spectateSecret);
|
||||||
|
typedef void (*joinRequestPtr)(const DiscordUser* request);
|
||||||
|
|
||||||
|
typedef struct DiscordEventHandlers {
|
||||||
|
readyPtr ready;
|
||||||
|
disconnectedPtr disconnected;
|
||||||
|
erroredPtr errored;
|
||||||
|
joinGamePtr joinGame;
|
||||||
|
spectateGamePtr spectateGame;
|
||||||
|
joinRequestPtr joinRequest;
|
||||||
|
} DiscordEventHandlers;
|
||||||
|
|
||||||
|
void Discord_Initialize(const char* applicationId,
|
||||||
|
DiscordEventHandlers* handlers,
|
||||||
|
int autoRegister,
|
||||||
|
const char* optionalSteamId);
|
||||||
|
|
||||||
|
void Discord_Shutdown(void);
|
||||||
|
|
||||||
|
void Discord_RunCallbacks(void);
|
||||||
|
|
||||||
|
void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||||
|
|
||||||
|
void Discord_ClearPresence(void);
|
||||||
|
|
||||||
|
void Discord_Respond(const char* userid, int reply);
|
||||||
|
|
||||||
|
void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local discordRPC = {} -- module table
|
||||||
|
|
||||||
|
-- proxy to detect garbage collection of the module
|
||||||
|
discordRPC.gcDummy = newproxy(true)
|
||||||
|
|
||||||
|
local function unpackDiscordUser(request)
|
||||||
|
return ffi.string(request.userId), ffi.string(request.username),
|
||||||
|
ffi.string(request.discriminator), ffi.string(request.avatar)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- callback proxies
|
||||||
|
-- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them
|
||||||
|
-- luajit.org/ext_ffi_semantics.html
|
||||||
|
local ready_proxy = ffi.cast("readyPtr", function(request)
|
||||||
|
if discordRPC.ready then
|
||||||
|
discordRPC.ready(unpackDiscordUser(request))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local disconnected_proxy = ffi.cast("disconnectedPtr", function(errorCode, message)
|
||||||
|
if discordRPC.disconnected then
|
||||||
|
discordRPC.disconnected(errorCode, ffi.string(message))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local errored_proxy = ffi.cast("erroredPtr", function(errorCode, message)
|
||||||
|
if discordRPC.errored then
|
||||||
|
discordRPC.errored(errorCode, ffi.string(message))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local joinGame_proxy = ffi.cast("joinGamePtr", function(joinSecret)
|
||||||
|
if discordRPC.joinGame then
|
||||||
|
discordRPC.joinGame(ffi.string(joinSecret))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local spectateGame_proxy = ffi.cast("spectateGamePtr", function(spectateSecret)
|
||||||
|
if discordRPC.spectateGame then
|
||||||
|
discordRPC.spectateGame(ffi.string(spectateSecret))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local joinRequest_proxy = ffi.cast("joinRequestPtr", function(request)
|
||||||
|
if discordRPC.joinRequest then
|
||||||
|
discordRPC.joinRequest(unpackDiscordUser(request))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- helpers
|
||||||
|
local function checkArg(arg, argType, argName, func, maybeNil)
|
||||||
|
assert(type(arg) == argType or (maybeNil and arg == nil),
|
||||||
|
string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"",
|
||||||
|
argName, func, argType))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkStrArg(arg, maxLen, argName, func, maybeNil)
|
||||||
|
if maxLen then
|
||||||
|
assert(type(arg) == "string" and arg:len() <= maxLen or (maybeNil and arg == nil),
|
||||||
|
string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d",
|
||||||
|
argName, func, maxLen))
|
||||||
|
else
|
||||||
|
checkArg(arg, "string", argName, func, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkIntArg(arg, maxBits, argName, func, maybeNil)
|
||||||
|
maxBits = math.min(maxBits or 32, 52) -- lua number (double) can only store integers < 2^53
|
||||||
|
local maxVal = 2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
|
||||||
|
assert(type(arg) == "number" and math.floor(arg) == arg
|
||||||
|
and arg < maxVal and arg >= -maxVal
|
||||||
|
or (maybeNil and arg == nil),
|
||||||
|
string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d",
|
||||||
|
argName, func, maxVal))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- function wrappers
|
||||||
|
function discordRPC.initialize(applicationId, autoRegister, optionalSteamId)
|
||||||
|
local func = "discordRPC.Initialize"
|
||||||
|
checkStrArg(applicationId, nil, "applicationId", func)
|
||||||
|
checkArg(autoRegister, "boolean", "autoRegister", func)
|
||||||
|
if optionalSteamId ~= nil then
|
||||||
|
checkStrArg(optionalSteamId, nil, "optionalSteamId", func)
|
||||||
|
end
|
||||||
|
|
||||||
|
local eventHandlers = ffi.new("struct DiscordEventHandlers")
|
||||||
|
eventHandlers.ready = ready_proxy
|
||||||
|
eventHandlers.disconnected = disconnected_proxy
|
||||||
|
eventHandlers.errored = errored_proxy
|
||||||
|
eventHandlers.joinGame = joinGame_proxy
|
||||||
|
eventHandlers.spectateGame = spectateGame_proxy
|
||||||
|
eventHandlers.joinRequest = joinRequest_proxy
|
||||||
|
|
||||||
|
discordRPClib.Discord_Initialize(applicationId, eventHandlers,
|
||||||
|
autoRegister and 1 or 0, optionalSteamId)
|
||||||
|
end
|
||||||
|
|
||||||
|
function discordRPC.shutdown()
|
||||||
|
discordRPClib.Discord_Shutdown()
|
||||||
|
end
|
||||||
|
|
||||||
|
function discordRPC.runCallbacks()
|
||||||
|
discordRPClib.Discord_RunCallbacks()
|
||||||
|
end
|
||||||
|
-- http://luajit.org/ext_ffi_semantics.html#callback :
|
||||||
|
-- It is not allowed, to let an FFI call into a C function (runCallbacks)
|
||||||
|
-- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready).
|
||||||
|
-- Usually this attempt is caught by the interpreter first and the C function
|
||||||
|
-- is blacklisted for compilation.
|
||||||
|
-- solution:
|
||||||
|
-- "Then you'll need to manually turn off JIT-compilation with jit.off() for
|
||||||
|
-- the surrounding Lua function that invokes such a message polling function."
|
||||||
|
jit.off(discordRPC.runCallbacks)
|
||||||
|
|
||||||
|
function discordRPC.updatePresence(presence)
|
||||||
|
local func = "discordRPC.updatePresence"
|
||||||
|
checkArg(presence, "table", "presence", func)
|
||||||
|
|
||||||
|
-- -1 for string length because of 0-termination
|
||||||
|
checkStrArg(presence.state, 127, "presence.state", func, true)
|
||||||
|
checkStrArg(presence.details, 127, "presence.details", func, true)
|
||||||
|
|
||||||
|
checkIntArg(presence.startTimestamp, 64, "presence.startTimestamp", func, true)
|
||||||
|
checkIntArg(presence.endTimestamp, 64, "presence.endTimestamp", func, true)
|
||||||
|
|
||||||
|
checkStrArg(presence.largeImageKey, 31, "presence.largeImageKey", func, true)
|
||||||
|
checkStrArg(presence.largeImageText, 127, "presence.largeImageText", func, true)
|
||||||
|
checkStrArg(presence.smallImageKey, 31, "presence.smallImageKey", func, true)
|
||||||
|
checkStrArg(presence.smallImageText, 127, "presence.smallImageText", func, true)
|
||||||
|
checkStrArg(presence.partyId, 127, "presence.partyId", func, true)
|
||||||
|
|
||||||
|
checkIntArg(presence.partySize, 32, "presence.partySize", func, true)
|
||||||
|
checkIntArg(presence.partyMax, 32, "presence.partyMax", func, true)
|
||||||
|
|
||||||
|
checkStrArg(presence.matchSecret, 127, "presence.matchSecret", func, true)
|
||||||
|
checkStrArg(presence.joinSecret, 127, "presence.joinSecret", func, true)
|
||||||
|
checkStrArg(presence.spectateSecret, 127, "presence.spectateSecret", func, true)
|
||||||
|
|
||||||
|
checkIntArg(presence.instance, 8, "presence.instance", func, true)
|
||||||
|
|
||||||
|
local cpresence = ffi.new("struct DiscordRichPresence")
|
||||||
|
cpresence.state = presence.state
|
||||||
|
cpresence.details = presence.details
|
||||||
|
cpresence.startTimestamp = presence.startTimestamp or 0
|
||||||
|
cpresence.endTimestamp = presence.endTimestamp or 0
|
||||||
|
cpresence.largeImageKey = presence.largeImageKey
|
||||||
|
cpresence.largeImageText = presence.largeImageText
|
||||||
|
cpresence.smallImageKey = presence.smallImageKey
|
||||||
|
cpresence.smallImageText = presence.smallImageText
|
||||||
|
cpresence.partyId = presence.partyId
|
||||||
|
cpresence.partySize = presence.partySize or 0
|
||||||
|
cpresence.partyMax = presence.partyMax or 0
|
||||||
|
cpresence.matchSecret = presence.matchSecret
|
||||||
|
cpresence.joinSecret = presence.joinSecret
|
||||||
|
cpresence.spectateSecret = presence.spectateSecret
|
||||||
|
cpresence.instance = presence.instance or 0
|
||||||
|
|
||||||
|
discordRPClib.Discord_UpdatePresence(cpresence)
|
||||||
|
end
|
||||||
|
|
||||||
|
function discordRPC.clearPresence()
|
||||||
|
discordRPClib.Discord_ClearPresence()
|
||||||
|
end
|
||||||
|
|
||||||
|
local replyMap = {
|
||||||
|
no = 0,
|
||||||
|
yes = 1,
|
||||||
|
ignore = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
-- maybe let reply take ints too (0, 1, 2) and add constants to the module
|
||||||
|
function discordRPC.respond(userId, reply)
|
||||||
|
checkStrArg(userId, nil, "userId", "discordRPC.respond")
|
||||||
|
assert(replyMap[reply], "Argument 'reply' to discordRPC.respond has to be one of \"yes\", \"no\" or \"ignore\"")
|
||||||
|
discordRPClib.Discord_Respond(userId, replyMap[reply])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- garbage collection callback
|
||||||
|
getmetatable(discordRPC.gcDummy).__gc = function()
|
||||||
|
discordRPC.shutdown()
|
||||||
|
ready_proxy:free()
|
||||||
|
disconnected_proxy:free()
|
||||||
|
errored_proxy:free()
|
||||||
|
joinGame_proxy:free()
|
||||||
|
spectateGame_proxy:free()
|
||||||
|
joinRequest_proxy:free()
|
||||||
|
end
|
||||||
|
|
||||||
|
return discordRPC
|
||||||
@@ -6,7 +6,7 @@ bgm = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local current_bgm = nil
|
local current_bgm = nil
|
||||||
local bgm_locked = false
|
local bgm_locked = true
|
||||||
|
|
||||||
function switchBGM(sound, subsound)
|
function switchBGM(sound, subsound)
|
||||||
if bgm_locked then return end
|
if bgm_locked then return end
|
||||||
|
|||||||
@@ -1,54 +1,77 @@
|
|||||||
backgrounds = {
|
backgrounds = {
|
||||||
[0] = love.graphics.newImage("res/backgrounds/0-quantum-foam.png"),
|
[0] = love.graphics.newImage("res/backgrounds/0.png"),
|
||||||
love.graphics.newImage("res/backgrounds/100-big-bang.png"),
|
love.graphics.newImage("res/backgrounds/100.png"),
|
||||||
love.graphics.newImage("res/backgrounds/200-spiral-galaxy.png"),
|
love.graphics.newImage("res/backgrounds/200.png"),
|
||||||
love.graphics.newImage("res/backgrounds/300-sun-and-dust.png"),
|
love.graphics.newImage("res/backgrounds/300.png"),
|
||||||
love.graphics.newImage("res/backgrounds/400-earth-and-moon.png"),
|
love.graphics.newImage("res/backgrounds/400.png"),
|
||||||
love.graphics.newImage("res/backgrounds/500-cambrian-explosion.png"),
|
love.graphics.newImage("res/backgrounds/500.png"),
|
||||||
love.graphics.newImage("res/backgrounds/600-dinosaurs.png"),
|
love.graphics.newImage("res/backgrounds/600.png"),
|
||||||
love.graphics.newImage("res/backgrounds/700-asteroid.png"),
|
love.graphics.newImage("res/backgrounds/700.png"),
|
||||||
love.graphics.newImage("res/backgrounds/800-human-fire.png"),
|
love.graphics.newImage("res/backgrounds/800.png"),
|
||||||
love.graphics.newImage("res/backgrounds/900-early-civilization.png"),
|
love.graphics.newImage("res/backgrounds/900.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1000-vikings.png"),
|
love.graphics.newImage("res/backgrounds/1000.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1100-crusades.png"),
|
love.graphics.newImage("res/backgrounds/1100.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1200-genghis-khan.png"),
|
love.graphics.newImage("res/backgrounds/1200.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1300-black-death.png"),
|
love.graphics.newImage("res/backgrounds/1300.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1400-columbus-discovery.png"),
|
love.graphics.newImage("res/backgrounds/1400.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1500-aztecas.png"),
|
love.graphics.newImage("res/backgrounds/1500.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1600-telescope.png"),
|
love.graphics.newImage("res/backgrounds/1600.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1700-american-revolution.png"),
|
love.graphics.newImage("res/backgrounds/1700.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1800-railways.png"),
|
love.graphics.newImage("res/backgrounds/1800.png"),
|
||||||
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
|
love.graphics.newImage("res/backgrounds/1900.png"),
|
||||||
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
|
title = love.graphics.newImage("res/backgrounds/title.png"),
|
||||||
|
input_config = love.graphics.newImage("res/backgrounds/options-input.png"),
|
||||||
|
game_config = love.graphics.newImage("res/backgrounds/options-game.png"),
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks = {
|
blocks = {
|
||||||
["2tie"] = {
|
["2tie"] = {
|
||||||
I = love.graphics.newImage("res/img/s1.png"),
|
R = love.graphics.newImage("res/img/s1.png"),
|
||||||
J = love.graphics.newImage("res/img/s4.png"),
|
O = love.graphics.newImage("res/img/s3.png"),
|
||||||
L = love.graphics.newImage("res/img/s3.png"),
|
Y = love.graphics.newImage("res/img/s7.png"),
|
||||||
O = love.graphics.newImage("res/img/s7.png"),
|
G = love.graphics.newImage("res/img/s6.png"),
|
||||||
S = love.graphics.newImage("res/img/s5.png"),
|
C = love.graphics.newImage("res/img/s2.png"),
|
||||||
T = love.graphics.newImage("res/img/s2.png"),
|
B = love.graphics.newImage("res/img/s4.png"),
|
||||||
Z = love.graphics.newImage("res/img/s6.png"),
|
M = love.graphics.newImage("res/img/s5.png"),
|
||||||
F = love.graphics.newImage("res/img/s9.png"),
|
F = love.graphics.newImage("res/img/s9.png"),
|
||||||
G = love.graphics.newImage("res/img/s9.png"),
|
A = love.graphics.newImage("res/img/s8.png"),
|
||||||
X = love.graphics.newImage("res/img/s9.png"),
|
X = love.graphics.newImage("res/img/s9.png"),
|
||||||
},
|
},
|
||||||
["bone"] = {
|
["bone"] = {
|
||||||
I = love.graphics.newImage("res/img/bone.png"),
|
R = love.graphics.newImage("res/img/bone.png"),
|
||||||
J = love.graphics.newImage("res/img/bone.png"),
|
|
||||||
L = love.graphics.newImage("res/img/bone.png"),
|
|
||||||
O = love.graphics.newImage("res/img/bone.png"),
|
O = love.graphics.newImage("res/img/bone.png"),
|
||||||
S = love.graphics.newImage("res/img/bone.png"),
|
Y = love.graphics.newImage("res/img/bone.png"),
|
||||||
T = love.graphics.newImage("res/img/bone.png"),
|
|
||||||
Z = love.graphics.newImage("res/img/bone.png"),
|
|
||||||
F = love.graphics.newImage("res/img/bone.png"),
|
|
||||||
G = love.graphics.newImage("res/img/bone.png"),
|
G = love.graphics.newImage("res/img/bone.png"),
|
||||||
|
C = love.graphics.newImage("res/img/bone.png"),
|
||||||
|
B = love.graphics.newImage("res/img/bone.png"),
|
||||||
|
M = love.graphics.newImage("res/img/bone.png"),
|
||||||
|
F = love.graphics.newImage("res/img/bone.png"),
|
||||||
|
A = love.graphics.newImage("res/img/bone.png"),
|
||||||
X = love.graphics.newImage("res/img/bone.png"),
|
X = love.graphics.newImage("res/img/bone.png"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColourSchemes = {
|
||||||
|
Arika = {
|
||||||
|
I = "R",
|
||||||
|
L = "O",
|
||||||
|
J = "B",
|
||||||
|
S = "M",
|
||||||
|
Z = "G",
|
||||||
|
O = "Y",
|
||||||
|
T = "C",
|
||||||
|
},
|
||||||
|
TTC = {
|
||||||
|
I = "C",
|
||||||
|
L = "O",
|
||||||
|
J = "B",
|
||||||
|
S = "G",
|
||||||
|
Z = "R",
|
||||||
|
O = "Y",
|
||||||
|
T = "M",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for name, blockset in pairs(blocks) do
|
for name, blockset in pairs(blocks) do
|
||||||
for shape, image in pairs(blockset) do
|
for shape, image in pairs(blockset) do
|
||||||
image:setFilter("nearest")
|
image:setFilter("nearest")
|
||||||
|
|||||||
58
load/rpc.lua
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
print("Loading discord RPC...")
|
||||||
|
DiscordRPC = {
|
||||||
|
loaded = false
|
||||||
|
}
|
||||||
|
local success, RPC = pcall(require, "libs.discordRPC")
|
||||||
|
if success then
|
||||||
|
DiscordRPC.loaded = true
|
||||||
|
DiscordRPC.appId = "599778517789573120"
|
||||||
|
|
||||||
|
function RPC.ready(userId, username, discriminator, avatar)
|
||||||
|
print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar))
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.disconnected(errorCode, message)
|
||||||
|
print(string.format("Discord: disconnected (%d: %s)", errorCode, message))
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.errored(errorCode, message)
|
||||||
|
print(string.format("Discord: error (%d: %s)", errorCode, message))
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.joinGame(joinSecret)
|
||||||
|
print(string.format("Discord: join (%s)", joinSecret))
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.spectateGame(spectateSecret)
|
||||||
|
print(string.format("Discord: spectate (%s)", spectateSecret))
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.joinRequest(userId, username, discriminator, avatar)
|
||||||
|
print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar))
|
||||||
|
RPC.respond(userId, "yes")
|
||||||
|
end
|
||||||
|
|
||||||
|
RPC.initialize(DiscordRPC.appId, true)
|
||||||
|
local now = os.time(os.date("*t"))
|
||||||
|
|
||||||
|
DiscordRPC.RPC = RPC
|
||||||
|
print("DiscordRPC successfully loaded.")
|
||||||
|
else
|
||||||
|
print("DiscordRPC failed to load!")
|
||||||
|
print(RPC)
|
||||||
|
end
|
||||||
|
|
||||||
|
DiscordRPC.presence = {
|
||||||
|
startTimestamp = now,
|
||||||
|
details = "Loading game...",
|
||||||
|
state = "",
|
||||||
|
largeImageKey = "icon2",
|
||||||
|
largeImageText = "Arcade Stacker",
|
||||||
|
smallImageKey = "",
|
||||||
|
smallImageText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function DiscordRPC:update(newstuff)
|
||||||
|
for k, v in pairs(newstuff) do self.presence[k] = v end
|
||||||
|
if self.loaded then self.RPC.updatePresence(self.presence) end
|
||||||
|
end
|
||||||
@@ -10,20 +10,48 @@ sounds = {
|
|||||||
},
|
},
|
||||||
move = love.audio.newSource("res/se/move.wav", "static"),
|
move = love.audio.newSource("res/se/move.wav", "static"),
|
||||||
bottom = love.audio.newSource("res/se/bottom.wav", "static"),
|
bottom = love.audio.newSource("res/se/bottom.wav", "static"),
|
||||||
|
cursor = love.audio.newSource("res/se/cursor.wav", "static"),
|
||||||
|
cursor_lr = love.audio.newSource("res/se/cursor_lr.wav", "static"),
|
||||||
|
main_decide = love.audio.newSource("res/se/main_decide.wav", "static"),
|
||||||
|
mode_decide = love.audio.newSource("res/se/mode_decide.wav", "static"),
|
||||||
|
lock = love.audio.newSource("res/se/lock.wav", "static"),
|
||||||
|
hold = love.audio.newSource("res/se/hold.wav", "static"),
|
||||||
|
erase = love.audio.newSource("res/se/erase.wav", "static"),
|
||||||
|
fall = love.audio.newSource("res/se/fall.wav", "static"),
|
||||||
|
ready = love.audio.newSource("res/se/ready.wav", "static"),
|
||||||
|
go = love.audio.newSource("res/se/go.wav", "static"),
|
||||||
|
irs = love.audio.newSource("res/se/irs.wav", "static"),
|
||||||
|
ihs = love.audio.newSource("res/se/ihs.wav", "static"),
|
||||||
}
|
}
|
||||||
|
|
||||||
function playSE(sound, subsound)
|
function playSE(sound, subsound)
|
||||||
if subsound == nil then
|
if subsound == nil then
|
||||||
sounds[sound]:setVolume(0.1)
|
sounds[sound]:setVolume(0.5)
|
||||||
if sounds[sound]:isPlaying() then
|
if sounds[sound]:isPlaying() then
|
||||||
sounds[sound]:stop()
|
sounds[sound]:stop()
|
||||||
end
|
end
|
||||||
sounds[sound]:play()
|
sounds[sound]:play()
|
||||||
else
|
else
|
||||||
sounds[sound][subsound]:setVolume(0.1)
|
sounds[sound][subsound]:setVolume(0.6)
|
||||||
if sounds[sound][subsound]:isPlaying() then
|
if sounds[sound][subsound]:isPlaying() then
|
||||||
sounds[sound][subsound]:stop()
|
sounds[sound][subsound]:stop()
|
||||||
end
|
end
|
||||||
sounds[sound][subsound]:play()
|
sounds[sound][subsound]:play()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function playSEOnce(sound, subsound)
|
||||||
|
if subsound == nil then
|
||||||
|
sounds[sound]:setVolume(0.5)
|
||||||
|
if sounds[sound]:isPlaying() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
sounds[sound]:play()
|
||||||
|
else
|
||||||
|
sounds[sound][subsound]:setVolume(0.5)
|
||||||
|
if sounds[sound][subsound]:isPlaying() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
sounds[sound][subsound]:play()
|
||||||
|
end
|
||||||
|
end
|
||||||
151
main.lua
@@ -1,6 +1,7 @@
|
|||||||
function love.load()
|
function love.load()
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
highscores = {}
|
highscores = {}
|
||||||
|
require "load.rpc"
|
||||||
require "load.graphics"
|
require "load.graphics"
|
||||||
require "load.fonts"
|
require "load.fonts"
|
||||||
require "load.sounds"
|
require "load.sounds"
|
||||||
@@ -14,14 +15,47 @@ function love.load()
|
|||||||
|
|
||||||
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
||||||
|
|
||||||
|
if not config.gamesettings then
|
||||||
|
config.gamesettings = {}
|
||||||
|
config["das_last_key"] = false
|
||||||
|
else
|
||||||
|
config["das_last_key"] = config.gamesettings.das_last_key == 2
|
||||||
|
end
|
||||||
|
for _, option in ipairs(GameConfigScene.options) do
|
||||||
|
if not config.gamesettings[option[1]] then
|
||||||
|
config.gamesettings[option[1]] = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if not config.input then
|
if not config.input then
|
||||||
config.input = {}
|
|
||||||
scene = InputConfigScene()
|
scene = InputConfigScene()
|
||||||
else
|
else
|
||||||
if config.current_mode then current_mode = config.current_mode end
|
if config.current_mode then current_mode = config.current_mode end
|
||||||
if config.current_ruleset then current_ruleset = config.current_ruleset end
|
if config.current_ruleset then current_ruleset = config.current_ruleset end
|
||||||
scene = TitleScene()
|
scene = TitleScene()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
game_modes = {}
|
||||||
|
mode_list = love.filesystem.getDirectoryItems("tetris/modes")
|
||||||
|
for i=1,#mode_list do
|
||||||
|
if(mode_list[i] ~= "gamemode.lua" and mode_list[i] ~= "unrefactored_modes") then
|
||||||
|
game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rulesets = {}
|
||||||
|
rule_list = love.filesystem.getDirectoryItems("tetris/rulesets")
|
||||||
|
for i=1,#rule_list do
|
||||||
|
if(rule_list[i] ~= "ruleset.lua" and rule_list[i] ~= "unrefactored_rulesets") then
|
||||||
|
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--sort mode/rule lists
|
||||||
|
local function padnum(d) return ("%03d%s"):format(#d, d) end
|
||||||
|
table.sort(game_modes, function(a,b)
|
||||||
|
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||||
|
table.sort(rulesets, function(a,b)
|
||||||
|
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local TARGET_FPS = 60
|
local TARGET_FPS = 60
|
||||||
@@ -80,13 +114,124 @@ function love.draw()
|
|||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key, scancode, isrepeat)
|
function love.keypressed(key, scancode)
|
||||||
-- global hotkeys
|
-- global hotkeys
|
||||||
if scancode == "f4" then
|
if scancode == "f4" then
|
||||||
config["fullscreen"] = not config["fullscreen"]
|
config["fullscreen"] = not config["fullscreen"]
|
||||||
love.window.setFullscreen(config["fullscreen"])
|
love.window.setFullscreen(config["fullscreen"])
|
||||||
|
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
|
||||||
|
scene = InputConfigScene()
|
||||||
|
-- function keys are reserved
|
||||||
|
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
|
||||||
|
return
|
||||||
|
-- escape is reserved for menu_back
|
||||||
|
elseif scancode == "escape" then
|
||||||
|
scene:onInputPress({input="menu_back", type="key", key=key, scancode=scancode})
|
||||||
|
-- pass any other key to the scene, with its configured mapping
|
||||||
else
|
else
|
||||||
scene:onKeyPress({key=key, scancode=scancode, isRepeat=isrepeat})
|
local input_pressed = nil
|
||||||
|
if config.input and config.input.keys then
|
||||||
|
input_pressed = config.input.keys[scancode]
|
||||||
|
end
|
||||||
|
scene:onInputPress({input=input_pressed, type="key", key=key, scancode=scancode})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.keyreleased(key, scancode)
|
||||||
|
-- escape is reserved for menu_back
|
||||||
|
if scancode == "escape" then
|
||||||
|
scene:onInputRelease({input="menu_back", type="key", key=key, scancode=scancode})
|
||||||
|
-- function keys are reserved
|
||||||
|
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
|
||||||
|
return
|
||||||
|
-- handle all other keys; tab is reserved, but the input config scene keeps it from getting configured as a game input, so pass tab to the scene here
|
||||||
|
else
|
||||||
|
local input_released = nil
|
||||||
|
if config.input and config.input.keys then
|
||||||
|
input_released = config.input.keys[scancode]
|
||||||
|
end
|
||||||
|
scene:onInputRelease({input=input_released, type="key", key=key, scancode=scancode})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickpressed(joystick, button)
|
||||||
|
local input_pressed = nil
|
||||||
|
if
|
||||||
|
config.input and
|
||||||
|
config.input.joysticks and
|
||||||
|
config.input.joysticks[joystick:getName()] and
|
||||||
|
config.input.joysticks[joystick:getName()].buttons
|
||||||
|
then
|
||||||
|
input_pressed = config.input.joysticks[joystick:getName()].buttons[button]
|
||||||
|
end
|
||||||
|
scene:onInputPress({input=input_pressed, type="joybutton", name=joystick:getName(), button=button})
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickreleased(joystick, button)
|
||||||
|
local input_released = nil
|
||||||
|
if
|
||||||
|
config.input and
|
||||||
|
config.input.joysticks and
|
||||||
|
config.input.joysticks[joystick:getName()] and
|
||||||
|
config.input.joysticks[joystick:getName()].buttons
|
||||||
|
then
|
||||||
|
input_released = config.input.joysticks[joystick:getName()].buttons[button]
|
||||||
|
end
|
||||||
|
scene:onInputRelease({input=input_released, type="joybutton", name=joystick:getName(), button=button})
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickaxis(joystick, axis, value)
|
||||||
|
local input_pressed = nil
|
||||||
|
local positive_released = nil
|
||||||
|
local negative_released = nil
|
||||||
|
if
|
||||||
|
config.input and
|
||||||
|
config.input.joysticks and
|
||||||
|
config.input.joysticks[joystick:getName()] and
|
||||||
|
config.input.joysticks[joystick:getName()].axes and
|
||||||
|
config.input.joysticks[joystick:getName()].axes[axis]
|
||||||
|
then
|
||||||
|
if math.abs(value) >= 0.5 then
|
||||||
|
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"]
|
||||||
|
end
|
||||||
|
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
|
||||||
|
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
|
||||||
|
end
|
||||||
|
if math.abs(value) >= 0.5 then
|
||||||
|
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
|
||||||
|
else
|
||||||
|
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
|
||||||
|
scene:onInputRelease({input=negative_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickhat(joystick, hat, direction)
|
||||||
|
local input_pressed = nil
|
||||||
|
local has_hat = false
|
||||||
|
if
|
||||||
|
config.input and
|
||||||
|
config.input.joysticks and
|
||||||
|
config.input.joysticks[joystick:getName()] and
|
||||||
|
config.input.joysticks[joystick:getName()].hats and
|
||||||
|
config.input.joysticks[joystick:getName()].hats[hat]
|
||||||
|
then
|
||||||
|
if direction ~= "c" then
|
||||||
|
input_pressed = config.input.joysticks[joystick:getName()].hats[hat][direction]
|
||||||
|
end
|
||||||
|
has_hat = true
|
||||||
|
end
|
||||||
|
if input_pressed then
|
||||||
|
scene:onInputPress({input=input_pressed, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||||
|
elseif has_hat then
|
||||||
|
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
|
||||||
|
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||||
|
end
|
||||||
|
elseif direction ~= "c" then
|
||||||
|
scene:onInputPress({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||||
|
else
|
||||||
|
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
|
||||||
|
scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
2
package.bat
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
tar -a -c -f cambridge.zip libs/binser.lua libs/classic.lua libs/discordRPC.lua load res scene tetris conf.lua main.lua scene.lua funcs.lua
|
||||||
|
rename cambridge.zip cambridge.love
|
||||||
4
release
@@ -4,6 +4,6 @@ mkdir dist/windows
|
|||||||
mkdir dist/win32
|
mkdir dist/win32
|
||||||
cp cambridge.love dist/
|
cp cambridge.love dist/
|
||||||
cat dist/windows/love.exe cambridge.love > dist/windows/cambridge.exe
|
cat dist/windows/love.exe cambridge.love > dist/windows/cambridge.exe
|
||||||
zip dist/cambridge-windows.zip dist/windows/* SOURCES.md LICENSE
|
zip dist/cambridge-windows.zip dist/windows/* SOURCES.md LICENSE.md
|
||||||
cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe
|
cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe
|
||||||
zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE
|
zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE.md
|
||||||
|
|||||||
26
release.bat
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
call package.bat
|
||||||
|
|
||||||
|
mkdir dist
|
||||||
|
mkdir dist\windows
|
||||||
|
mkdir dist\windows\libs
|
||||||
|
mkdir dist\win32
|
||||||
|
mkdir dist\win32\libs
|
||||||
|
|
||||||
|
copy /b dist\windows\love.exe+cambridge.love dist\windows\cambridge.exe
|
||||||
|
copy /b dist\win32\love.exe+cambridge.love dist\win32\cambridge.exe
|
||||||
|
|
||||||
|
copy libs\discord-rpc.dll dist\windows\libs
|
||||||
|
copy libs\discord-rpc.dll dist\win32\libs
|
||||||
|
|
||||||
|
copy SOURCES.md dist\windows
|
||||||
|
copy LICENSE.md dist\windows
|
||||||
|
copy SOURCES.md dist\win32
|
||||||
|
copy LICENSE.md dist\win32
|
||||||
|
|
||||||
|
cd dist\windows
|
||||||
|
tar -a -c -f ..\cambridge-windows.zip cambridge.exe *.dll libs *.md
|
||||||
|
cd ..\..
|
||||||
|
|
||||||
|
cd dist\win32
|
||||||
|
tar -a -c -f ..\cambridge-win32.zip cambridge.exe *.dll libs *.md
|
||||||
|
cd ..\..
|
||||||
0
res/backgrounds/0-quantum-foam.png → res/backgrounds/0.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
0
res/backgrounds/100-big-bang.png → res/backgrounds/100.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
0
res/backgrounds/1000-vikings.png → res/backgrounds/1000.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
0
res/backgrounds/1100-crusades.png → res/backgrounds/1100.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.9 MiB After Width: | Height: | Size: 2.9 MiB |
0
res/backgrounds/1200-genghis-khan.png → res/backgrounds/1200.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
0
res/backgrounds/1300-black-death.png → res/backgrounds/1300.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
0
res/backgrounds/1400-columbus-discovery.png → res/backgrounds/1400.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
0
res/backgrounds/1500-aztecas.png → res/backgrounds/1500.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
0
res/backgrounds/1600-telescope.png → res/backgrounds/1600.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
0
res/backgrounds/1700-american-revolution.png → res/backgrounds/1700.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
0
res/backgrounds/1800-railways.png → res/backgrounds/1800.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
0
res/backgrounds/1900-world-wide-web.png → res/backgrounds/1900.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
0
res/backgrounds/200-spiral-galaxy.png → res/backgrounds/200.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
0
res/backgrounds/300-sun-and-dust.png → res/backgrounds/300.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
0
res/backgrounds/400-earth-and-moon.png → res/backgrounds/400.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
0
res/backgrounds/500-cambrian-explosion.png → res/backgrounds/500.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
0
res/backgrounds/600-dinosaurs.png → res/backgrounds/600.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 3.1 MiB |
0
res/backgrounds/700-asteroid.png → res/backgrounds/700.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
0
res/backgrounds/800-human-fire.png → res/backgrounds/800.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.7 MiB |
0
res/backgrounds/900-early-civilization.png → res/backgrounds/900.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 3.6 MiB |
BIN
res/backgrounds/options-game.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
res/backgrounds/options-input.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
res/backgrounds/title.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
BIN
res/img/bonew.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
res/img/s8.png
Normal file
|
After Width: | Height: | Size: 233 B |
BIN
res/se/cursor.wav
Normal file
BIN
res/se/cursor_lr.wav
Normal file
BIN
res/se/erase.wav
Normal file
BIN
res/se/fall.wav
Normal file
BIN
res/se/go.wav
Normal file
BIN
res/se/hold.wav
Normal file
BIN
res/se/ihs.wav
Normal file
BIN
res/se/irs.wav
Normal file
BIN
res/se/lock.wav
Normal file
BIN
res/se/main_decide.wav
Normal file
BIN
res/se/mode_decide.wav
Normal file
BIN
res/se/ready.wav
Normal file
@@ -5,10 +5,12 @@ Scene = Object:extend()
|
|||||||
function Scene:new() end
|
function Scene:new() end
|
||||||
function Scene:update() end
|
function Scene:update() end
|
||||||
function Scene:render() end
|
function Scene:render() end
|
||||||
function Scene:onKeyPress() end
|
function Scene:onInputPress() end
|
||||||
|
function Scene:onInputRelease() 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"
|
||||||
ConfigScene = require "scene.config"
|
GameConfigScene = require "scene.game_config"
|
||||||
TitleScene = require "scene.title"
|
TitleScene = require "scene.title"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function ConfigScene:changeOption(rel)
|
|||||||
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
|
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function ConfigScene:onKeyPress(e)
|
function ConfigScene:onInputPress(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ConfigScene
|
return ConfigScene
|
||||||
|
|||||||
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:onInputPress(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ExitScene
|
||||||
|
|
||||||
@@ -1,26 +1,40 @@
|
|||||||
local GameScene = Scene:extend()
|
local GameScene = Scene:extend()
|
||||||
|
|
||||||
|
GameScene.title = "Game"
|
||||||
|
|
||||||
require 'load.save'
|
require 'load.save'
|
||||||
|
|
||||||
function GameScene:new(game_mode, ruleset)
|
function GameScene:new(game_mode, ruleset)
|
||||||
|
self.retry_mode = game_mode
|
||||||
|
self.retry_ruleset = ruleset
|
||||||
self.game = game_mode()
|
self.game = game_mode()
|
||||||
self.ruleset = ruleset()
|
self.ruleset = ruleset()
|
||||||
self.game:initialize(self.ruleset)
|
self.game:initialize(self.ruleset)
|
||||||
|
self.inputs = {
|
||||||
|
left=false,
|
||||||
|
right=false,
|
||||||
|
up=false,
|
||||||
|
down=false,
|
||||||
|
rotate_left=false,
|
||||||
|
rotate_left2=false,
|
||||||
|
rotate_right=false,
|
||||||
|
rotate_right2=false,
|
||||||
|
rotate_180=false,
|
||||||
|
hold=false,
|
||||||
|
}
|
||||||
|
DiscordRPC:update({
|
||||||
|
details = self.game.rpc_details,
|
||||||
|
state = self.game.name,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameScene:update()
|
function GameScene:update()
|
||||||
if love.window.hasFocus() then
|
if love.window.hasFocus() then
|
||||||
self.game:update({
|
local inputs = {}
|
||||||
left = love.keyboard.isScancodeDown(config.input.left),
|
for input, value in pairs(self.inputs) do
|
||||||
right = love.keyboard.isScancodeDown(config.input.right),
|
inputs[input] = value
|
||||||
up = love.keyboard.isScancodeDown(config.input.up),
|
end
|
||||||
down = love.keyboard.isScancodeDown(config.input.down),
|
self.game:update(inputs, self.ruleset)
|
||||||
rotate_left = love.keyboard.isScancodeDown(config.input.rotate_left),
|
|
||||||
rotate_left2 = love.keyboard.isScancodeDown(config.input.rotate_left2),
|
|
||||||
rotate_right = love.keyboard.isScancodeDown(config.input.rotate_right),
|
|
||||||
rotate_right2 = love.keyboard.isScancodeDown(config.input.rotate_right2),
|
|
||||||
rotate_180 = love.keyboard.isScancodeDown(config.input.rotate_180),
|
|
||||||
hold = love.keyboard.isScancodeDown(config.input.hold),
|
|
||||||
}, self.ruleset)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.game.grid:update()
|
self.game.grid:update()
|
||||||
@@ -45,6 +59,7 @@ function GameScene:render()
|
|||||||
self.game:drawScoringInfo()
|
self.game:drawScoringInfo()
|
||||||
|
|
||||||
-- ready/go graphics
|
-- ready/go graphics
|
||||||
|
|
||||||
if self.game.ready_frames <= 100 and self.game.ready_frames > 52 then
|
if self.game.ready_frames <= 100 and self.game.ready_frames > 52 then
|
||||||
love.graphics.draw(misc_graphics["ready"], 144 - 50, 240 - 14)
|
love.graphics.draw(misc_graphics["ready"], 144 - 50, 240 - 14)
|
||||||
elseif self.game.ready_frames <= 50 and self.game.ready_frames > 2 then
|
elseif self.game.ready_frames <= 50 and self.game.ready_frames > 2 then
|
||||||
@@ -53,15 +68,30 @@ function GameScene:render()
|
|||||||
|
|
||||||
self.game:drawCustom()
|
self.game:drawCustom()
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
if config.gamesettings.display_gamemode == 1 then
|
||||||
|
love.graphics.printf(self.game.name .. " - " .. self.ruleset.name, 0, 460, 640, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameScene:onKeyPress(e)
|
function GameScene:onInputPress(e)
|
||||||
if (self.game.completed) and
|
if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
|
||||||
e.scancode == "return" 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 = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) or ModeSelectScene()
|
||||||
|
elseif e.input == "retry" then
|
||||||
|
scene = GameScene(self.retry_mode, self.retry_ruleset)
|
||||||
|
elseif e.input == "menu_back" then
|
||||||
scene = ModeSelectScene()
|
scene = ModeSelectScene()
|
||||||
|
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
|
||||||
|
self.inputs[e.input] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function GameScene:onInputRelease(e)
|
||||||
|
if e.input and string.sub(e.input, 1, 5) ~= "menu_" then
|
||||||
|
self.inputs[e.input] = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
85
scene/game_config.lua
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
local ConfigScene = Scene:extend()
|
||||||
|
|
||||||
|
ConfigScene.title = "Game Settings"
|
||||||
|
|
||||||
|
require 'load.save'
|
||||||
|
|
||||||
|
ConfigScene.options = {
|
||||||
|
-- this serves as reference to what the options' values mean i guess?
|
||||||
|
{"manlock", "Manual locking",{"Per ruleset","Per gamemode","Harddrop", "Softdrop"}},
|
||||||
|
{"piece_colour", "Piece Colours", {"Per ruleset","Arika" ,"TTC"}},
|
||||||
|
{"world_reverse","A Button Rotation", {"Left" ,"Auto" ,"Right"}},
|
||||||
|
{"display_gamemode", "Display Gamemode", {"On", "Off"}},
|
||||||
|
{"next_se", "Next Piece SFX", {"On", "Off"}},
|
||||||
|
{"das_last_key", "DAS Switch", {"Default", "Instant"}},
|
||||||
|
{"synchroes_allowed", "Synchroes", {"Per ruleset", "On", "Off"}},
|
||||||
|
{"diagonal_input", "Diagonal Input", {"On", "Off"}}
|
||||||
|
}
|
||||||
|
local optioncount = #ConfigScene.options
|
||||||
|
|
||||||
|
function ConfigScene:new()
|
||||||
|
-- load current config
|
||||||
|
self.config = config.input
|
||||||
|
self.highlight = 1
|
||||||
|
|
||||||
|
DiscordRPC:update({
|
||||||
|
details = "In menus",
|
||||||
|
state = "Changing game settings",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function ConfigScene:update()
|
||||||
|
config["das_last_key"] = config.gamesettings.das_last_key == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function ConfigScene:render()
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
love.graphics.draw(
|
||||||
|
backgrounds["game_config"],
|
||||||
|
0, 0, 0,
|
||||||
|
0.5, 0.5
|
||||||
|
)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_4)
|
||||||
|
love.graphics.print("GAME SETTINGS", 80, 40)
|
||||||
|
|
||||||
|
love.graphics.setColor(1, 1, 1, 0.5)
|
||||||
|
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
for i, option in ipairs(ConfigScene.options) do
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left")
|
||||||
|
for j, setting in ipairs(option[3]) do
|
||||||
|
love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5)
|
||||||
|
love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ConfigScene:onInputPress(e)
|
||||||
|
if e.input == "menu_decide" or e.scancode == "return" then
|
||||||
|
playSE("mode_decide")
|
||||||
|
saveConfig()
|
||||||
|
scene = TitleScene()
|
||||||
|
elseif e.input == "up" or e.scancode == "up" then
|
||||||
|
playSE("cursor")
|
||||||
|
self.highlight = Mod1(self.highlight-1, optioncount)
|
||||||
|
elseif e.input == "down" or e.scancode == "down" then
|
||||||
|
playSE("cursor")
|
||||||
|
self.highlight = Mod1(self.highlight+1, optioncount)
|
||||||
|
elseif e.input == "left" or e.scancode == "left" then
|
||||||
|
playSE("cursor_lr")
|
||||||
|
local option = ConfigScene.options[self.highlight]
|
||||||
|
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3])
|
||||||
|
elseif e.input == "right" or e.scancode == "right" then
|
||||||
|
playSE("cursor_lr")
|
||||||
|
local option = ConfigScene.options[self.highlight]
|
||||||
|
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3])
|
||||||
|
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||||
|
loadSave()
|
||||||
|
scene = TitleScene()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ConfigScene
|
||||||
@@ -5,6 +5,8 @@ ConfigScene.title = "Input Config"
|
|||||||
require 'load.save'
|
require 'load.save'
|
||||||
|
|
||||||
local configurable_inputs = {
|
local configurable_inputs = {
|
||||||
|
"menu_decide",
|
||||||
|
"menu_back",
|
||||||
"left",
|
"left",
|
||||||
"right",
|
"right",
|
||||||
"up",
|
"up",
|
||||||
@@ -15,47 +17,145 @@ local configurable_inputs = {
|
|||||||
"rotate_right2",
|
"rotate_right2",
|
||||||
"rotate_180",
|
"rotate_180",
|
||||||
"hold",
|
"hold",
|
||||||
|
"retry",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local function newSetInputs()
|
||||||
|
local set_inputs = {}
|
||||||
|
for i, input in ipairs(configurable_inputs) do
|
||||||
|
set_inputs[input] = false
|
||||||
|
end
|
||||||
|
return set_inputs
|
||||||
|
end
|
||||||
|
|
||||||
function ConfigScene:new()
|
function ConfigScene:new()
|
||||||
-- load current config
|
|
||||||
self.config = config.input
|
|
||||||
self.input_state = 1
|
self.input_state = 1
|
||||||
|
self.set_inputs = newSetInputs()
|
||||||
|
self.new_input = {}
|
||||||
|
self.axis_timer = 0
|
||||||
|
|
||||||
|
DiscordRPC:update({
|
||||||
|
details = "In menus",
|
||||||
|
state = "Changing input config",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function ConfigScene:update()
|
function ConfigScene:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function ConfigScene:render()
|
function ConfigScene:render()
|
||||||
love.graphics.setFont(font_3x5_2)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
for i, input in pairs(configurable_inputs) do
|
love.graphics.draw(
|
||||||
if config.input[input] then
|
backgrounds["input_config"],
|
||||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
0, 0, 0,
|
||||||
love.graphics.printf(
|
0.5, 0.5
|
||||||
love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")",
|
|
||||||
240, 50 + i * 20, 200, "left"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
for i, input in ipairs(configurable_inputs) do
|
||||||
|
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||||
|
if self.set_inputs[input] then
|
||||||
|
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.input_state > table.getn(configurable_inputs) then
|
if self.input_state > table.getn(configurable_inputs) then
|
||||||
love.graphics.print("press enter to confirm, delete to retry")
|
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
|
||||||
else
|
else
|
||||||
love.graphics.print("press key for " .. configurable_inputs[self.input_state])
|
love.graphics.print("press key or joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
|
||||||
|
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.axis_timer = self.axis_timer + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addJoystick(input, name)
|
||||||
|
if not input.joysticks then
|
||||||
|
input.joysticks = {}
|
||||||
|
end
|
||||||
|
if not input.joysticks[name] then
|
||||||
|
input.joysticks[name] = {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ConfigScene:onKeyPress(e)
|
function ConfigScene:onInputPress(e)
|
||||||
if self.input_state > table.getn(configurable_inputs) then
|
if e.type == "key" then
|
||||||
|
-- function keys, escape, and tab are reserved and can't be remapped
|
||||||
|
if e.scancode == "escape" and config.input then
|
||||||
|
-- cancel only if there was an input config already
|
||||||
|
scene = TitleScene()
|
||||||
|
elseif self.input_state > table.getn(configurable_inputs) then
|
||||||
if e.scancode == "return" then
|
if e.scancode == "return" then
|
||||||
-- save, then load next scene
|
-- save new input, then load next scene
|
||||||
|
config.input = self.new_input
|
||||||
saveConfig()
|
saveConfig()
|
||||||
scene = TitleScene()
|
scene = TitleScene()
|
||||||
elseif e.scancode == "delete" or e.scancode == "backspace" then
|
elseif e.scancode == "delete" or e.scancode == "backspace" then
|
||||||
|
-- retry
|
||||||
self.input_state = 1
|
self.input_state = 1
|
||||||
|
self.set_inputs = newSetInputs()
|
||||||
|
self.new_input = {}
|
||||||
end
|
end
|
||||||
else
|
elseif e.scancode == "tab" then
|
||||||
config.input[configurable_inputs[self.input_state]] = e.scancode
|
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
|
||||||
self.input_state = self.input_state + 1
|
self.input_state = self.input_state + 1
|
||||||
|
elseif e.scancode ~= "escape" then
|
||||||
|
-- all other keys can be configured
|
||||||
|
if not self.new_input.keys then
|
||||||
|
self.new_input.keys = {}
|
||||||
|
end
|
||||||
|
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
|
||||||
|
self.new_input.keys[e.scancode] = configurable_inputs[self.input_state]
|
||||||
|
self.input_state = self.input_state + 1
|
||||||
|
end
|
||||||
|
elseif string.sub(e.type, 1, 3) == "joy" then
|
||||||
|
if self.input_state <= table.getn(configurable_inputs) then
|
||||||
|
if e.type == "joybutton" then
|
||||||
|
addJoystick(self.new_input, e.name)
|
||||||
|
if not self.new_input.joysticks[e.name].buttons then
|
||||||
|
self.new_input.joysticks[e.name].buttons = {}
|
||||||
|
end
|
||||||
|
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||||
|
"jbtn " ..
|
||||||
|
e.button ..
|
||||||
|
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||||
|
self.new_input.joysticks[e.name].buttons[e.button] = configurable_inputs[self.input_state]
|
||||||
|
self.input_state = self.input_state + 1
|
||||||
|
elseif e.type == "joyaxis" then
|
||||||
|
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
|
||||||
|
addJoystick(self.new_input, e.name)
|
||||||
|
if not self.new_input.joysticks[e.name].axes then
|
||||||
|
self.new_input.joysticks[e.name].axes = {}
|
||||||
|
end
|
||||||
|
if not self.new_input.joysticks[e.name].axes[e.axis] then
|
||||||
|
self.new_input.joysticks[e.name].axes[e.axis] = {}
|
||||||
|
end
|
||||||
|
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||||
|
"jaxis " ..
|
||||||
|
(e.value >= 1 and "+" or "-") .. e.axis ..
|
||||||
|
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||||
|
self.new_input.joysticks[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
|
||||||
|
self.input_state = self.input_state + 1
|
||||||
|
self.last_axis = e.axis
|
||||||
|
self.axis_timer = 0
|
||||||
|
end
|
||||||
|
elseif e.type == "joyhat" then
|
||||||
|
if e.direction ~= "c" then
|
||||||
|
addJoystick(self.new_input, e.name)
|
||||||
|
if not self.new_input.joysticks[e.name].hats then
|
||||||
|
self.new_input.joysticks[e.name].hats = {}
|
||||||
|
end
|
||||||
|
if not self.new_input.joysticks[e.name].hats[e.hat] then
|
||||||
|
self.new_input.joysticks[e.name].hats[e.hat] = {}
|
||||||
|
end
|
||||||
|
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||||
|
"jhat " ..
|
||||||
|
e.hat .. " " .. e.direction ..
|
||||||
|
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
|
||||||
|
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
|
||||||
|
self.input_state = self.input_state + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
64
scene/mode_select.lua
Normal file → Executable file
@@ -5,43 +5,16 @@ ModeSelectScene.title = "Game Start"
|
|||||||
current_mode = 1
|
current_mode = 1
|
||||||
current_ruleset = 1
|
current_ruleset = 1
|
||||||
|
|
||||||
game_modes = {
|
|
||||||
require 'tetris.modes.marathon_2020',
|
|
||||||
require 'tetris.modes.survival_2020',
|
|
||||||
require 'tetris.modes.strategy',
|
|
||||||
require 'tetris.modes.interval_training',
|
|
||||||
require 'tetris.modes.pacer_test',
|
|
||||||
require 'tetris.modes.demon_mode',
|
|
||||||
require 'tetris.modes.phantom_mania',
|
|
||||||
require 'tetris.modes.phantom_mania2',
|
|
||||||
require 'tetris.modes.phantom_mania_n',
|
|
||||||
require 'tetris.modes.race_40',
|
|
||||||
require 'tetris.modes.marathon_a1',
|
|
||||||
require 'tetris.modes.marathon_a2',
|
|
||||||
require 'tetris.modes.marathon_a3',
|
|
||||||
require 'tetris.modes.marathon_ax4',
|
|
||||||
require 'tetris.modes.marathon_c89',
|
|
||||||
require 'tetris.modes.survival_a1',
|
|
||||||
require 'tetris.modes.survival_a2',
|
|
||||||
require 'tetris.modes.survival_a3',
|
|
||||||
}
|
|
||||||
|
|
||||||
rulesets = {
|
|
||||||
require 'tetris.rulesets.cambridge',
|
|
||||||
require 'tetris.rulesets.arika',
|
|
||||||
require 'tetris.rulesets.arika_ti',
|
|
||||||
require 'tetris.rulesets.standard_exp',
|
|
||||||
--require 'tetris.rulesets.bonkers',
|
|
||||||
--require 'tetris.rulesets.shirase',
|
|
||||||
--require 'tetris.rulesets.super302',
|
|
||||||
}
|
|
||||||
|
|
||||||
function ModeSelectScene:new()
|
function ModeSelectScene:new()
|
||||||
self.menu_state = {
|
self.menu_state = {
|
||||||
mode = current_mode,
|
mode = current_mode,
|
||||||
ruleset = current_ruleset,
|
ruleset = current_ruleset,
|
||||||
select = "mode",
|
select = "mode",
|
||||||
}
|
}
|
||||||
|
DiscordRPC:update({
|
||||||
|
details = "In menus",
|
||||||
|
state = "Choosing a mode",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function ModeSelectScene:update()
|
function ModeSelectScene:update()
|
||||||
@@ -59,14 +32,14 @@ function ModeSelectScene:render()
|
|||||||
elseif self.menu_state.select == "ruleset" then
|
elseif self.menu_state.select == "ruleset" then
|
||||||
love.graphics.setColor(1, 1, 1, 0.25)
|
love.graphics.setColor(1, 1, 1, 0.25)
|
||||||
end
|
end
|
||||||
love.graphics.rectangle("fill", 20, 78 + 20 * self.menu_state.mode, 240, 22)
|
love.graphics.rectangle("fill", 20, 258, 240, 22)
|
||||||
|
|
||||||
if self.menu_state.select == "mode" then
|
if self.menu_state.select == "mode" then
|
||||||
love.graphics.setColor(1, 1, 1, 0.25)
|
love.graphics.setColor(1, 1, 1, 0.25)
|
||||||
elseif self.menu_state.select == "ruleset" then
|
elseif self.menu_state.select == "ruleset" then
|
||||||
love.graphics.setColor(1, 1, 1, 0.5)
|
love.graphics.setColor(1, 1, 1, 0.5)
|
||||||
end
|
end
|
||||||
love.graphics.rectangle("fill", 340, 78 + 20 * self.menu_state.ruleset, 200, 22)
|
love.graphics.rectangle("fill", 340, 258, 200, 22)
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
@@ -74,28 +47,37 @@ function ModeSelectScene:render()
|
|||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
love.graphics.setFont(font_3x5_2)
|
||||||
for idx, mode in pairs(game_modes) do
|
for idx, mode in pairs(game_modes) do
|
||||||
love.graphics.printf(mode.name, 40, 80 + 20 * idx, 200, "left")
|
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
|
||||||
|
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
for idx, ruleset in pairs(rulesets) do
|
for idx, ruleset in pairs(rulesets) do
|
||||||
love.graphics.printf(ruleset.name, 360, 80 + 20 * idx, 160, "left")
|
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
|
||||||
|
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ModeSelectScene:onKeyPress(e)
|
function ModeSelectScene:onInputPress(e)
|
||||||
if e.scancode == "return" and e.isRepeat == false then
|
if e.input == "menu_decide" or e.scancode == "return" then
|
||||||
current_mode = self.menu_state.mode
|
current_mode = self.menu_state.mode
|
||||||
current_ruleset = self.menu_state.ruleset
|
current_ruleset = self.menu_state.ruleset
|
||||||
config.current_mode = current_mode
|
config.current_mode = current_mode
|
||||||
config.current_ruleset = current_ruleset
|
config.current_ruleset = current_ruleset
|
||||||
|
playSE("mode_decide")
|
||||||
saveConfig()
|
saveConfig()
|
||||||
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset])
|
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset])
|
||||||
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
|
elseif e.input == "up" or e.scancode == "up" then
|
||||||
self:changeOption(-1)
|
self:changeOption(-1)
|
||||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
playSE("cursor")
|
||||||
|
elseif e.input == "down" or e.scancode == "down" then
|
||||||
self:changeOption(1)
|
self:changeOption(1)
|
||||||
elseif (e.scancode == config.input["left"] or e.scancode == "left") or
|
playSE("cursor")
|
||||||
(e.scancode == config.input["right"] or e.scancode == "right") then
|
elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then
|
||||||
self:switchSelect()
|
self:switchSelect()
|
||||||
|
playSE("cursor_lr")
|
||||||
|
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||||
|
scene = TitleScene()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,31 @@ local TitleScene = Scene:extend()
|
|||||||
local main_menu_screens = {
|
local main_menu_screens = {
|
||||||
ModeSelectScene,
|
ModeSelectScene,
|
||||||
InputConfigScene,
|
InputConfigScene,
|
||||||
|
GameConfigScene,
|
||||||
|
ExitScene,
|
||||||
|
}
|
||||||
|
|
||||||
|
local mainmenuidle = {
|
||||||
|
"Idle",
|
||||||
|
"On title screen",
|
||||||
|
"On main menu screen",
|
||||||
|
"Twiddling their thumbs",
|
||||||
|
"Admiring the main menu's BG",
|
||||||
|
"Waiting for spring to come",
|
||||||
|
"Actually not playing",
|
||||||
|
"Contemplating collecting stars",
|
||||||
|
"Preparing to put the block!!",
|
||||||
|
"Having a nap",
|
||||||
|
"In menus",
|
||||||
|
"Bottom text",
|
||||||
}
|
}
|
||||||
|
|
||||||
function TitleScene:new()
|
function TitleScene:new()
|
||||||
self.main_menu_state = 1
|
self.main_menu_state = 1
|
||||||
|
DiscordRPC:update({
|
||||||
|
details = "In menus",
|
||||||
|
state = mainmenuidle[math.random(#mainmenuidle)],
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function TitleScene:update()
|
function TitleScene:update()
|
||||||
@@ -36,13 +57,18 @@ function TitleScene:changeOption(rel)
|
|||||||
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
|
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function TitleScene:onKeyPress(e)
|
function TitleScene:onInputPress(e)
|
||||||
if e.scancode == "return" and e.isRepeat == false then
|
if e.input == "menu_decide" or e.scancode == "return" then
|
||||||
|
playSE("main_decide")
|
||||||
scene = main_menu_screens[self.main_menu_state]()
|
scene = main_menu_screens[self.main_menu_state]()
|
||||||
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
|
elseif e.input == "up" or e.scancode == "up" then
|
||||||
self:changeOption(-1)
|
self:changeOption(-1)
|
||||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
playSE("cursor")
|
||||||
|
elseif e.input == "down" or e.scancode == "down" then
|
||||||
self:changeOption(1)
|
self:changeOption(1)
|
||||||
|
playSE("cursor")
|
||||||
|
elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
|
||||||
|
love.event.quit()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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 = "" }
|
||||||
|
local block = { skin = "2tie", colour = "A" }
|
||||||
|
|
||||||
function Grid:new()
|
function Grid:new()
|
||||||
self.grid = {}
|
self.grid = {}
|
||||||
@@ -26,8 +28,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)
|
||||||
@@ -46,7 +55,7 @@ function Grid:canPlacePiece(piece)
|
|||||||
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
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -58,12 +67,12 @@ function Grid:canPlaceBigPiece(piece)
|
|||||||
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 >= 5 or x < 0 or y >= 12 or y < 0 or
|
if (
|
||||||
self.grid[y * 2 + 1][x * 2 + 1] ~= empty or
|
self:isOccupied(x * 2 + 0, y * 2 + 0)
|
||||||
self.grid[y * 2 + 1][x * 2 + 2] ~= empty or
|
or self:isOccupied(x * 2 + 1, y * 2 + 0)
|
||||||
self.grid[y * 2 + 2][x * 2 + 1] ~= empty or
|
or self:isOccupied(x * 2 + 0, y * 2 + 1)
|
||||||
self.grid[y * 2 + 2][x * 2 + 2] ~= empty
|
or self:isOccupied(x * 2 + 1, y * 2 + 1)
|
||||||
then
|
) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -80,7 +89,7 @@ function Grid:canPlacePieceInVisibleGrid(piece)
|
|||||||
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
|
||||||
@@ -133,14 +142,49 @@ function Grid:copyBottomRow()
|
|||||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
for col = 1, 10 do
|
for col = 1, 10 do
|
||||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or {
|
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
|
||||||
skin = self.grid[23][col].skin,
|
|
||||||
colour = "G"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Grid:garbageRise(row_vals)
|
||||||
|
for row = 1, 23 do
|
||||||
|
self.grid[row] = self.grid[row+1]
|
||||||
|
self.grid_age[row] = self.grid_age[row+1]
|
||||||
|
end
|
||||||
|
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||||
|
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
for col = 1, 10 do
|
||||||
|
self.grid[24][col] = (row_vals[col] == "e") and empty or block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:applyFourWide()
|
||||||
|
for row = 1, 24 do
|
||||||
|
local x = self.grid[row]
|
||||||
|
x[1] = x[1]~=block and block or x[1]
|
||||||
|
x[2] = x[2]~=block and block or x[2]
|
||||||
|
x[3] = x[3]~=block and block or x[3]
|
||||||
|
x[8] = x[8]~=block and block or x[8]
|
||||||
|
x[9] = x[9]~=block and block or x[9]
|
||||||
|
x[10] = x[10]~=block and block or x[10]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:applyCeiling(lines)
|
||||||
|
for row = 1, lines do
|
||||||
|
for col = 1, 9 do
|
||||||
|
self.grid[row][col] = block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:clearSpecificRow(row)
|
||||||
|
for col = 1, 10 do
|
||||||
|
self.grid[row][col] = empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Grid:applyPiece(piece)
|
function Grid:applyPiece(piece)
|
||||||
if piece.big then
|
if piece.big then
|
||||||
self:applyBigPiece(piece)
|
self:applyBigPiece(piece)
|
||||||
@@ -150,12 +194,14 @@ function Grid:applyPiece(piece)
|
|||||||
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
|
||||||
|
if y + 1 > 0 then
|
||||||
self.grid[y+1][x+1] = {
|
self.grid[y+1][x+1] = {
|
||||||
skin = piece.skin,
|
skin = piece.skin,
|
||||||
colour = piece.shape
|
colour = piece.colour
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Grid:applyBigPiece(piece)
|
function Grid:applyBigPiece(piece)
|
||||||
offsets = piece:getBlockOffsets()
|
offsets = piece:getBlockOffsets()
|
||||||
@@ -164,14 +210,68 @@ function Grid:applyBigPiece(piece)
|
|||||||
y = piece.position.y + offset.y
|
y = piece.position.y + offset.y
|
||||||
for a = 1, 2 do
|
for a = 1, 2 do
|
||||||
for b = 1, 2 do
|
for b = 1, 2 do
|
||||||
|
if y*2+a > 0 then
|
||||||
self.grid[y*2+a][x*2+b] = {
|
self.grid[y*2+a][x*2+b] = {
|
||||||
skin = piece.skin,
|
skin = piece.skin,
|
||||||
colour = piece.shape
|
colour = piece.colour
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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:checkStackHeight()
|
||||||
|
for i = 0, 23 do
|
||||||
|
for j = 0, 9 do
|
||||||
|
if self:isOccupied(j, i) then return 24 - i end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
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
|
||||||
@@ -184,14 +284,18 @@ function Grid:update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Grid:draw()
|
function Grid:draw()
|
||||||
for y = 1, 24 do
|
for y = 5, 24 do
|
||||||
for x = 1, 10 do
|
for x = 1, 10 do
|
||||||
if self.grid[y][x] ~= empty then
|
if self.grid[y][x] ~= empty then
|
||||||
if self.grid_age[y][x] < 1 then
|
if self.grid_age[y][x] < 2 then
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
|
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
|
||||||
|
else
|
||||||
|
if self.grid[y][x].skin == "bone" then
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
else
|
else
|
||||||
love.graphics.setColor(0.5, 0.5, 0.5, 1)
|
love.graphics.setColor(0.5, 0.5, 0.5, 1)
|
||||||
|
end
|
||||||
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
||||||
end
|
end
|
||||||
if self.grid[y][x].skin ~= "bone" then
|
if self.grid[y][x].skin ~= "bone" then
|
||||||
@@ -215,19 +319,45 @@ function Grid:draw()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Grid:drawInvisible(opacity_function, garbage_opacity_function)
|
function Grid:drawOutline()
|
||||||
for y = 1, 24 do
|
for y = 5, 24 do
|
||||||
|
for x = 1, 10 do
|
||||||
|
if self.grid[y][x] ~= empty then
|
||||||
|
love.graphics.setColor(0.8, 0.8, 0.8, 1)
|
||||||
|
love.graphics.setLineWidth(1)
|
||||||
|
if y > 1 and self.grid[y-1][x] == empty then
|
||||||
|
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||||
|
end
|
||||||
|
if y < 24 and self.grid[y+1][x] == empty then
|
||||||
|
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||||
|
end
|
||||||
|
if x > 1 and self.grid[y][x-1] == empty then
|
||||||
|
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||||
|
end
|
||||||
|
if x < 10 and self.grid[y][x+1] == empty then
|
||||||
|
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
|
||||||
|
lock_flash = lock_flash == nil and true or lock_flash
|
||||||
|
brightness = brightness == nil and 0.5 or brightness
|
||||||
|
for y = 5, 24 do
|
||||||
for x = 1, 10 do
|
for x = 1, 10 do
|
||||||
if self.grid[y][x] ~= empty then
|
if self.grid[y][x] ~= empty then
|
||||||
if self.grid[y][x].colour == "X" then
|
if self.grid[y][x].colour == "X" then
|
||||||
opacity = 1
|
opacity = 1
|
||||||
elseif garbage_opacity_function and self.grid[y][x].colour == "G" then
|
elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
|
||||||
opacity = garbage_opacity_function(self.grid_age[y][x])
|
opacity = garbage_opacity_function(self.grid_age[y][x])
|
||||||
else
|
else
|
||||||
opacity = opacity_function(self.grid_age[y][x])
|
opacity = opacity_function(self.grid_age[y][x])
|
||||||
end
|
end
|
||||||
love.graphics.setColor(0.5, 0.5, 0.5, opacity)
|
love.graphics.setColor(brightness, brightness, brightness, opacity)
|
||||||
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
||||||
|
if lock_flash then
|
||||||
if opacity > 0 and self.grid[y][x].colour ~= "X" then
|
if opacity > 0 and self.grid[y][x].colour ~= "X" then
|
||||||
love.graphics.setColor(0.64, 0.64, 0.64)
|
love.graphics.setColor(0.64, 0.64, 0.64)
|
||||||
love.graphics.setLineWidth(1)
|
love.graphics.setLineWidth(1)
|
||||||
@@ -248,5 +378,6 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return Grid
|
return Grid
|
||||||
|
|||||||
@@ -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, big)
|
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
self.position = position
|
self.position = position
|
||||||
@@ -10,6 +10,7 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay
|
|||||||
self.gravity = gravity
|
self.gravity = gravity
|
||||||
self.lock_delay = lock_delay
|
self.lock_delay = lock_delay
|
||||||
self.skin = skin
|
self.skin = skin
|
||||||
|
self.colour = colour
|
||||||
self.ghost = false
|
self.ghost = false
|
||||||
self.locked = false
|
self.locked = false
|
||||||
self.big = big
|
self.big = big
|
||||||
@@ -21,7 +22,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.big
|
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -31,7 +32,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.big
|
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,12 +78,15 @@ function Piece:setRelativeRotation(rot)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Piece:moveInGrid(step, squares, grid)
|
function Piece:moveInGrid(step, squares, grid, instant)
|
||||||
local moved = false
|
local moved = false
|
||||||
for x = 1, squares do
|
for x = 1, squares do
|
||||||
if grid:canPlacePiece(self:withOffset(step)) then
|
if grid:canPlacePiece(self:withOffset(step)) then
|
||||||
moved = true
|
moved = true
|
||||||
self:setOffset(step)
|
self:setOffset(step)
|
||||||
|
if instant then
|
||||||
|
self:dropToBottom(grid)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -97,7 +101,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(math.huge, grid)
|
||||||
self.gravity = 0
|
self.gravity = 0
|
||||||
if self.position.y > piece_y then
|
if self.position.y > piece_y then
|
||||||
-- if it got dropped any, also reset lock delay
|
-- if it got dropped any, also reset lock delay
|
||||||
@@ -148,13 +152,13 @@ function Piece:draw(opacity, brightness, grid, partial_das)
|
|||||||
local y = self.position.y + offset.y
|
local y = self.position.y + offset.y
|
||||||
if self.big then
|
if self.big then
|
||||||
love.graphics.draw(
|
love.graphics.draw(
|
||||||
blocks[self.skin][self.shape],
|
blocks[self.skin][self.colour],
|
||||||
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
|
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
|
||||||
0, 2, 2
|
0, 2, 2
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
love.graphics.draw(
|
love.graphics.draw(
|
||||||
blocks[self.skin][self.shape],
|
blocks[self.skin][self.colour],
|
||||||
64+x*16+partial_das, 16+y*16+gravity_offset
|
64+x*16+partial_das, 16+y*16+gravity_offset
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
307
tetris/modes/big_a2.lua
Executable file
@@ -0,0 +1,307 @@
|
|||||||
|
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.grade = 0
|
||||||
|
self.grade_points = 0
|
||||||
|
self.grade_point_decay_counter = 0
|
||||||
|
|
||||||
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
|
self.lock_drop = false
|
||||||
|
self.lock_hard_drop = false
|
||||||
|
self.enable_hold = false
|
||||||
|
self.next_queue_length = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getARE()
|
||||||
|
if self.level < 700 then return 27
|
||||||
|
elseif self.level < 800 then return 18
|
||||||
|
else return 14 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getLineARE()
|
||||||
|
if self.level < 600 then return 27
|
||||||
|
elseif self.level < 700 then return 18
|
||||||
|
elseif self.level < 800 then return 14
|
||||||
|
else return 8 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getDasLimit()
|
||||||
|
if self.level < 500 then return 15
|
||||||
|
elseif self.level < 900 then return 9
|
||||||
|
else return 7 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getLineClearDelay()
|
||||||
|
if self.level < 500 then return 40
|
||||||
|
elseif self.level < 600 then return 25
|
||||||
|
elseif self.level < 700 then return 16
|
||||||
|
elseif self.level < 800 then return 12
|
||||||
|
else return 6 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getLockDelay()
|
||||||
|
if self.level < 900 then return 30
|
||||||
|
else return 17 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getGravity()
|
||||||
|
if (self.level < 30) then return 4/256
|
||||||
|
elseif (self.level < 35) then return 6/256
|
||||||
|
elseif (self.level < 40) then return 8/256
|
||||||
|
elseif (self.level < 50) then return 10/256
|
||||||
|
elseif (self.level < 60) then return 12/256
|
||||||
|
elseif (self.level < 70) then return 16/256
|
||||||
|
elseif (self.level < 80) then return 32/256
|
||||||
|
elseif (self.level < 90) then return 48/256
|
||||||
|
elseif (self.level < 100) then return 64/256
|
||||||
|
elseif (self.level < 120) then return 80/256
|
||||||
|
elseif (self.level < 140) then return 96/256
|
||||||
|
elseif (self.level < 160) then return 112/256
|
||||||
|
elseif (self.level < 170) then return 128/256
|
||||||
|
elseif (self.level < 200) then return 144/256
|
||||||
|
elseif (self.level < 220) then return 4/256
|
||||||
|
elseif (self.level < 230) then return 32/256
|
||||||
|
elseif (self.level < 233) then return 64/256
|
||||||
|
elseif (self.level < 236) then return 96/256
|
||||||
|
elseif (self.level < 239) then return 128/256
|
||||||
|
elseif (self.level < 243) then return 160/256
|
||||||
|
elseif (self.level < 247) then return 192/256
|
||||||
|
elseif (self.level < 251) then return 224/256
|
||||||
|
elseif (self.level < 300) then return 1
|
||||||
|
elseif (self.level < 330) then return 2
|
||||||
|
elseif (self.level < 360) then return 3
|
||||||
|
elseif (self.level < 400) then return 4
|
||||||
|
elseif (self.level < 420) then return 5
|
||||||
|
elseif (self.level < 450) then return 4
|
||||||
|
elseif (self.level < 500) then return 3
|
||||||
|
else return 20
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:advanceOneFrame()
|
||||||
|
if self.clear then
|
||||||
|
self.roll_frames = self.roll_frames + 1
|
||||||
|
if self.roll_frames < 0 then return false end
|
||||||
|
if self.roll_frames > 3694 then
|
||||||
|
self.completed = true
|
||||||
|
end
|
||||||
|
elseif self.ready_frames == 0 then
|
||||||
|
self.frames = self.frames + 1
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:onPieceEnter()
|
||||||
|
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||||
|
self.level = self.level + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||||
|
cleared_row_count = cleared_row_count / 2
|
||||||
|
self.level = math.min(self.level + cleared_row_count, 999)
|
||||||
|
if self.level == 999 and not self.clear then
|
||||||
|
self.clear = true
|
||||||
|
self.grid:clear()
|
||||||
|
self.roll_frames = -150
|
||||||
|
end
|
||||||
|
self.lock_drop = self.level >= 900
|
||||||
|
self.lock_hard_drop = self.level >= 900
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
|
cleared_lines = cleared_lines / 2
|
||||||
|
self:updateGrade(cleared_lines)
|
||||||
|
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||||
|
if cleared_lines > 0 then
|
||||||
|
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||||
|
self.score = self.score + (
|
||||||
|
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||||
|
cleared_lines * self.combo * self.bravo
|
||||||
|
)
|
||||||
|
else
|
||||||
|
self.combo = 1
|
||||||
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
function MarathonA2Game:updateGrade(cleared_lines)
|
||||||
|
if self.clear then return end
|
||||||
|
if cleared_lines == 0 then
|
||||||
|
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||||
|
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||||
|
self.grade_point_decay_counter = 0
|
||||||
|
self.grade_points = math.max(0, self.grade_points - 1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.grade_points = self.grade_points + (
|
||||||
|
math.ceil(
|
||||||
|
grade_point_bonuses[self.grade + 1][cleared_lines] *
|
||||||
|
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
|
||||||
|
) * (1 + math.floor(self.level / 250))
|
||||||
|
)
|
||||||
|
if self.grade_points >= 100 and self.grade < 31 then
|
||||||
|
self.grade_points = 0
|
||||||
|
self.grade = self.grade + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getLetterGrade()
|
||||||
|
local grade = grade_conversion[self.grade]
|
||||||
|
if grade < 9 then
|
||||||
|
return tostring(9 - grade)
|
||||||
|
elseif grade < 18 then
|
||||||
|
return "S" .. tostring(grade - 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MarathonA2Game.rollOpacityFunction = function(age)
|
||||||
|
if age < 240 then return 1
|
||||||
|
elseif age > 300 then return 0
|
||||||
|
else return 1 - (age - 240) / 60 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:drawGrid(ruleset)
|
||||||
|
if self.clear and not (self.completed or self.game_over) then
|
||||||
|
self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
|
||||||
|
else
|
||||||
|
self.grid:draw()
|
||||||
|
if self.piece ~= nil and self.level < 100 then
|
||||||
|
self:drawGhostPiece(ruleset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:drawScoringInfo()
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_2)
|
||||||
|
love.graphics.print(
|
||||||
|
self.das.direction .. " " ..
|
||||||
|
self.das.frames .. " " ..
|
||||||
|
strTrueValues(self.prev_inputs)
|
||||||
|
)
|
||||||
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||||
|
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||||
|
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||||
|
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_3x5_3)
|
||||||
|
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||||
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||||
|
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||||
|
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||||
|
|
||||||
|
love.graphics.setFont(font_8x11)
|
||||||
|
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getHighscoreData()
|
||||||
|
return {
|
||||||
|
grade = grade_conversion[self.grade],
|
||||||
|
score = self.score,
|
||||||
|
level = self.level,
|
||||||
|
frames = self.frames,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getSectionEndLevel()
|
||||||
|
if self.level >= 900 then return 999
|
||||||
|
else return math.floor(self.level / 100 + 1) * 100 end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:getBackground()
|
||||||
|
return math.floor(self.level / 100)
|
||||||
|
end
|
||||||
|
|
||||||
|
return MarathonA2Game
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
require 'funcs'
|
|
||||||
|
|
||||||
local GameMode = require 'tetris.modes.gamemode'
|
|
||||||
local Piece = require 'tetris.components.piece'
|
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
|
||||||
|
|
||||||
local DemonModeGame = GameMode:extend()
|
|
||||||
|
|
||||||
DemonModeGame.name = "Demon Mode"
|
|
||||||
DemonModeGame.hash = "DemonMode"
|
|
||||||
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function DemonModeGame:new()
|
|
||||||
DemonModeGame.super:new()
|
|
||||||
self.roll_frames = 0
|
|
||||||
self.combo = 1
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
|
||||||
|
|
||||||
self.grade = 0
|
|
||||||
self.section_start_time = 0
|
|
||||||
self.section_times = { [0] = 0 }
|
|
||||||
self.section_tetris_count = 0
|
|
||||||
self.section_tries = 0
|
|
||||||
|
|
||||||
self.enable_hold = true
|
|
||||||
self.lock_drop = true
|
|
||||||
self.next_queue_length = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getARE()
|
|
||||||
if self.level < 500 then return 30
|
|
||||||
elseif self.level < 600 then return 25
|
|
||||||
elseif self.level < 700 then return 15
|
|
||||||
elseif self.level < 800 then return 14
|
|
||||||
elseif self.level < 900 then return 12
|
|
||||||
elseif self.level < 1000 then return 11
|
|
||||||
elseif self.level < 1100 then return 10
|
|
||||||
elseif self.level < 1300 then return 8
|
|
||||||
elseif self.level < 1400 then return 6
|
|
||||||
elseif self.level < 1700 then return 4
|
|
||||||
elseif self.level < 1800 then return 3
|
|
||||||
elseif self.level < 1900 then return 2
|
|
||||||
elseif self.level < 2000 then return 1
|
|
||||||
else return 0 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getLineARE()
|
|
||||||
return self:getARE()
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getDasLimit()
|
|
||||||
if self.level < 500 then return 15
|
|
||||||
elseif self.level < 1000 then return 10
|
|
||||||
elseif self.level < 1500 then return 5
|
|
||||||
elseif self.level < 1700 then return 4
|
|
||||||
elseif self.level < 1900 then return 3
|
|
||||||
elseif self.level < 2000 then return 2
|
|
||||||
else return 1 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getLineClearDelay()
|
|
||||||
if self.level < 600 then return 15
|
|
||||||
elseif self.level < 800 then return 10
|
|
||||||
elseif self.level < 1000 then return 8
|
|
||||||
elseif self.level < 1500 then return 5
|
|
||||||
elseif self.level < 1700 then return 3
|
|
||||||
elseif self.level < 1900 then return 2
|
|
||||||
elseif self.level < 2000 then return 1
|
|
||||||
else return 0 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getLockDelay()
|
|
||||||
if self.level < 100 then return 30
|
|
||||||
elseif self.level < 200 then return 25
|
|
||||||
elseif self.level < 300 then return 22
|
|
||||||
elseif self.level < 400 then return 20
|
|
||||||
elseif self.level < 1000 then return 15
|
|
||||||
elseif self.level < 1200 then return 10
|
|
||||||
elseif self.level < 1400 then return 9
|
|
||||||
elseif self.level < 1500 then return 8
|
|
||||||
elseif self.level < 1600 then return 7
|
|
||||||
elseif self.level < 1700 then return 6
|
|
||||||
elseif self.level < 1800 then return 5
|
|
||||||
elseif self.level < 1900 then return 4
|
|
||||||
elseif self.level < 2000 then return 3
|
|
||||||
else return 2 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getGravity()
|
|
||||||
return 20
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getSectionForLevel(level)
|
|
||||||
return math.floor(level / 100) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local cleared_row_levels = {1, 3, 6, 10}
|
|
||||||
|
|
||||||
function DemonModeGame:advanceOneFrame()
|
|
||||||
if self.clear then
|
|
||||||
self.roll_frames = self.roll_frames + 1
|
|
||||||
if self.roll_frames < 0 then
|
|
||||||
return false
|
|
||||||
elseif self.roll_frames >= 1337 then
|
|
||||||
self.completed = true
|
|
||||||
end
|
|
||||||
elseif self.ready_frames == 0 then
|
|
||||||
self.frames = self.frames + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:onPieceEnter()
|
|
||||||
if (self.level % 100 ~= 99) and self.frames ~= 0 then
|
|
||||||
self.level = self.level + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:onLineClear(cleared_row_count)
|
|
||||||
if cleared_row_count == 4 then
|
|
||||||
self.section_tetris_count = self.section_tetris_count + 1
|
|
||||||
end
|
|
||||||
local advanced_levels = cleared_row_levels[cleared_row_count]
|
|
||||||
if not self.clear then
|
|
||||||
self:updateSectionTimes(self.level, self.level + advanced_levels)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:updateSectionTimes(old_level, new_level)
|
|
||||||
local section = math.floor(old_level / 100) + 1
|
|
||||||
if math.floor(old_level / 100) < math.floor(new_level / 100) then
|
|
||||||
-- If at least one Tetris in this section hasn't been made,
|
|
||||||
-- deny section passage.
|
|
||||||
if old_level > 500 then
|
|
||||||
if self.section_tetris_count == 0 then
|
|
||||||
self.level = 100 * math.floor(old_level / 100)
|
|
||||||
self.section_tries = self.section_tries + 1
|
|
||||||
else
|
|
||||||
self.level = math.min(new_level, 2500)
|
|
||||||
-- if this is first try (no denials, add a grade)
|
|
||||||
if self.section_tries == 0 then
|
|
||||||
self.grade = self.grade + 1
|
|
||||||
end
|
|
||||||
self.section_tries = 0
|
|
||||||
self.section_tetris_count = 0
|
|
||||||
-- record new section
|
|
||||||
section_time = self.frames - self.section_start_time
|
|
||||||
table.insert(self.section_times, section_time)
|
|
||||||
self.section_start_time = self.frames
|
|
||||||
-- maybe clear
|
|
||||||
if self.level == 2500 and not self.clear then
|
|
||||||
self.clear = true
|
|
||||||
self.grid:clear()
|
|
||||||
self.roll_frames = -150
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif old_level < 100 then
|
|
||||||
-- If section time is under cutoff, skip to level 500.
|
|
||||||
if self.frames < sp(1,00) then
|
|
||||||
self.level = 500
|
|
||||||
self.grade = 5
|
|
||||||
self.section_tries = 0
|
|
||||||
self.section_tetris_count = 0
|
|
||||||
else
|
|
||||||
self.level = math.min(new_level, 2500)
|
|
||||||
end
|
|
||||||
-- record new section
|
|
||||||
section_time = self.frames - self.section_start_time
|
|
||||||
table.insert(self.section_times, section_time)
|
|
||||||
self.section_start_time = self.frames
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.level = math.min(new_level, 2500)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame: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
|
|
||||||
|
|
||||||
local letter_grades = {
|
|
||||||
[0] = "", "D", "C", "B", "A",
|
|
||||||
"S", "S-A", "S-B", "S-C", "S-D",
|
|
||||||
"X", "X-A", "X-B", "X-C", "X-D",
|
|
||||||
"W", "W-A", "W-B", "W-C", "W-D",
|
|
||||||
"Master", "MasterS", "MasterX", "MasterW", "Grand Master",
|
|
||||||
"Demon Master"
|
|
||||||
}
|
|
||||||
|
|
||||||
function DemonModeGame:getLetterGrade()
|
|
||||||
return letter_grades[self.grade]
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:drawGrid()
|
|
||||||
if self.clear and not (self.completed or self.game_over) then
|
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
|
||||||
else
|
|
||||||
self.grid:draw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
DemonModeGame.rollOpacityFunction = function(age)
|
|
||||||
if age > 4 then return 0
|
|
||||||
else return 1 - age / 4 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:drawScoringInfo()
|
|
||||||
DemonModeGame.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 .. " " ..
|
|
||||||
st(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")
|
|
||||||
|
|
||||||
-- draw section time data
|
|
||||||
local current_section = getSectionForLevel(self.level)
|
|
||||||
self:drawSectionTimesWithSecondary(current_section)
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
|
||||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
|
||||||
love.graphics.printf(string.format("%.2f", self.level / 100), 240, 340, 70, "right")
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getHighscoreData()
|
|
||||||
return {
|
|
||||||
grade = self.grade,
|
|
||||||
level = self.level,
|
|
||||||
frames = self.frames,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function DemonModeGame:getBackground()
|
|
||||||
return math.floor(self.level / 100)
|
|
||||||
end
|
|
||||||
|
|
||||||
return DemonModeGame
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
local Object = require 'libs.classic'
|
local Object = require 'libs.classic'
|
||||||
require 'funcs'
|
require 'funcs'
|
||||||
|
|
||||||
|
local playedReadySE = false
|
||||||
|
local playedGoSE = false
|
||||||
|
|
||||||
local Grid = require 'tetris.components.grid'
|
local Grid = require 'tetris.components.grid'
|
||||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||||
|
|
||||||
@@ -40,9 +43,19 @@ function GameMode:new()
|
|||||||
self.draw_section_times = false
|
self.draw_section_times = false
|
||||||
self.draw_secondary_section_times = false
|
self.draw_secondary_section_times = false
|
||||||
self.big_mode = false
|
self.big_mode = false
|
||||||
|
self.irs = true
|
||||||
|
self.ihs = true
|
||||||
|
self.rpc_details = "In game"
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
-- 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
|
||||||
|
self.lock_on_soft_drop = false
|
||||||
|
self.lock_on_hard_drop = false
|
||||||
self.hold_queue = nil
|
self.hold_queue = nil
|
||||||
self.held = false
|
self.held = false
|
||||||
self.section_start_time = 0
|
self.section_start_time = 0
|
||||||
@@ -50,13 +63,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
|
||||||
@@ -66,6 +72,7 @@ function GameMode:getLineClearDelay() return 40 end
|
|||||||
function GameMode:getDasLimit() return 15 end
|
function GameMode:getDasLimit() return 15 end
|
||||||
|
|
||||||
function GameMode:getNextPiece(ruleset)
|
function GameMode:getNextPiece(ruleset)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
skin = "2tie",
|
skin = "2tie",
|
||||||
shape = self.randomizer:nextPiece(),
|
shape = self.randomizer:nextPiece(),
|
||||||
@@ -75,9 +82,12 @@ 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
|
||||||
|
self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
|
||||||
|
self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock]
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:update(inputs, ruleset)
|
function GameMode:update(inputs, ruleset)
|
||||||
@@ -90,11 +100,31 @@ function GameMode:update(inputs, ruleset)
|
|||||||
end
|
end
|
||||||
if self.completed then return end
|
if self.completed then return end
|
||||||
|
|
||||||
|
if config.gamesettings.diagonal_input == 2 then
|
||||||
|
if inputs["left"] or inputs["right"] then
|
||||||
|
inputs["up"] = false
|
||||||
|
inputs["down"] = false
|
||||||
|
elseif inputs["up"] or inputs["down"] then
|
||||||
|
inputs["left"] = false
|
||||||
|
inputs["right"] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- advance one frame
|
-- advance one frame
|
||||||
if self:advanceOneFrame(inputs) == false then return end
|
if self:advanceOneFrame(inputs, ruleset) == false then return end
|
||||||
|
|
||||||
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
|
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
|
||||||
|
|
||||||
|
-- set attempt flags
|
||||||
|
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end
|
||||||
|
if
|
||||||
|
inputs["rotate_left"] or inputs["rotate_right"] or
|
||||||
|
inputs["rotate_left2"] or inputs["rotate_right2"] or
|
||||||
|
inputs["rotate_180"]
|
||||||
|
then
|
||||||
|
self:onAttemptPieceRotate(self.piece)
|
||||||
|
end
|
||||||
|
|
||||||
if self.piece == nil then
|
if self.piece == nil then
|
||||||
self:processDelays(inputs, ruleset)
|
self:processDelays(inputs, ruleset)
|
||||||
else
|
else
|
||||||
@@ -102,7 +132,7 @@ function GameMode:update(inputs, ruleset)
|
|||||||
self:whilePieceActive()
|
self:whilePieceActive()
|
||||||
local gravity = self:getGravity()
|
local gravity = self:getGravity()
|
||||||
|
|
||||||
if self.enable_hold and inputs["hold"] == true and self.held == false then
|
if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then
|
||||||
self:hold(inputs, ruleset)
|
self:hold(inputs, ruleset)
|
||||||
self.prev_inputs = inputs
|
self.prev_inputs = inputs
|
||||||
return
|
return
|
||||||
@@ -131,7 +161,7 @@ function GameMode:update(inputs, ruleset)
|
|||||||
self.piece:isDropBlocked(self.grid) and
|
self.piece:isDropBlocked(self.grid) and
|
||||||
not self.hard_drop_locked then
|
not self.hard_drop_locked then
|
||||||
self:onHardDrop(piece_dy)
|
self:onHardDrop(piece_dy)
|
||||||
if self.instant_hard_drop then
|
if self.lock_on_hard_drop then
|
||||||
self.piece.locked = true
|
self.piece.locked = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -140,7 +170,7 @@ function GameMode:update(inputs, ruleset)
|
|||||||
self:onSoftDrop(piece_dy)
|
self:onSoftDrop(piece_dy)
|
||||||
if self.piece:isDropBlocked(self.grid) and
|
if self.piece:isDropBlocked(self.grid) and
|
||||||
not self.drop_locked and
|
not self.drop_locked and
|
||||||
self.instant_soft_drop
|
self.lock_on_soft_drop
|
||||||
then
|
then
|
||||||
self.piece.locked = true
|
self.piece.locked = true
|
||||||
end
|
end
|
||||||
@@ -161,6 +191,7 @@ function GameMode:update(inputs, ruleset)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if cleared_row_count > 0 then
|
if cleared_row_count > 0 then
|
||||||
|
playSE("erase")
|
||||||
self.lcd = self:getLineClearDelay()
|
self.lcd = self:getLineClearDelay()
|
||||||
self.are = self:getLineARE()
|
self.are = self:getLineARE()
|
||||||
if self.lcd == 0 then
|
if self.lcd == 0 then
|
||||||
@@ -194,8 +225,14 @@ end
|
|||||||
|
|
||||||
-- event functions
|
-- event functions
|
||||||
function GameMode:whilePieceActive() end
|
function GameMode:whilePieceActive() end
|
||||||
function GameMode:onPieceLock(piece, cleared_row_count) end
|
function GameMode:onAttemptPieceMove(piece) end
|
||||||
|
function GameMode:onAttemptPieceRotate(piece) end
|
||||||
|
function GameMode:onPieceLock(piece, cleared_row_count)
|
||||||
|
playSE("lock")
|
||||||
|
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
|
||||||
|
|
||||||
@@ -211,8 +248,25 @@ function GameMode:onGameOver()
|
|||||||
switchBGM(nil)
|
switchBGM(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:chargeDAS(inputs)
|
-- DAS functions
|
||||||
if inputs[self.das.direction] == true then
|
|
||||||
|
function GameMode:startRightDAS()
|
||||||
|
self.move = "right"
|
||||||
|
self.das = { direction = "right", frames = 0 }
|
||||||
|
if self:getDasLimit() == 0 then
|
||||||
|
self:continueDAS()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function GameMode:startLeftDAS()
|
||||||
|
self.move = "left"
|
||||||
|
self.das = { direction = "left", frames = 0 }
|
||||||
|
if self:getDasLimit() == 0 then
|
||||||
|
self:continueDAS()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function GameMode:continueDAS()
|
||||||
local das_frames = self.das.frames + 1
|
local das_frames = self.das.frames + 1
|
||||||
if das_frames >= self:getDasLimit() then
|
if das_frames >= self:getDasLimit() then
|
||||||
if self.das.direction == "left" then
|
if self.das.direction == "left" then
|
||||||
@@ -226,34 +280,89 @@ function GameMode:chargeDAS(inputs)
|
|||||||
self.move = "none"
|
self.move = "none"
|
||||||
self.das.frames = das_frames
|
self.das.frames = das_frames
|
||||||
end
|
end
|
||||||
elseif inputs["right"] == true then
|
end
|
||||||
self.move = "right"
|
|
||||||
self.das = { direction = "right", frames = 0 }
|
function GameMode:stopDAS()
|
||||||
elseif inputs["left"] == true then
|
|
||||||
self.move = "left"
|
|
||||||
self.das = { direction = "left", frames = 0 }
|
|
||||||
else
|
|
||||||
self.move = "none"
|
self.move = "none"
|
||||||
self.das = { direction = "none", frames = -1 }
|
self.das = { direction = "none", frames = -1 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function GameMode:chargeDAS(inputs)
|
||||||
|
if config["das_last_key"] then
|
||||||
|
if inputs["right"] == true and self.das.direction ~= "right" and not self.prev_inputs["right"] then
|
||||||
|
self:startRightDAS()
|
||||||
|
elseif inputs["left"] == true and self.das.direction ~= "left" and not self.prev_inputs["left"] then
|
||||||
|
self:startLeftDAS()
|
||||||
|
elseif inputs[self.das.direction] == true then
|
||||||
|
self:continueDAS()
|
||||||
|
else
|
||||||
|
self:stopDAS()
|
||||||
|
end
|
||||||
|
else -- default behaviour, das first key pressed
|
||||||
|
if inputs[self.das.direction] == true then
|
||||||
|
self:continueDAS()
|
||||||
|
elseif inputs["right"] == true then
|
||||||
|
self:startRightDAS()
|
||||||
|
elseif inputs["left"] == true then
|
||||||
|
self:startLeftDAS()
|
||||||
|
else
|
||||||
|
self:stopDAS()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:processDelays(inputs, ruleset, drop_speed)
|
function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||||
|
if self.ready_frames == 100 then
|
||||||
|
playedReadySE = false
|
||||||
|
playedGoSE = false
|
||||||
|
end
|
||||||
if self.ready_frames > 0 then
|
if self.ready_frames > 0 then
|
||||||
|
if not playedReadySE then
|
||||||
|
playedReadySE = true
|
||||||
|
playSEOnce("ready")
|
||||||
|
end
|
||||||
self.ready_frames = self.ready_frames - 1
|
self.ready_frames = self.ready_frames - 1
|
||||||
|
if self.ready_frames == 50 and not playedGoSE then
|
||||||
|
playedGoSE = true
|
||||||
|
playSEOnce("go")
|
||||||
|
end
|
||||||
if self.ready_frames == 0 then
|
if self.ready_frames == 0 then
|
||||||
self:initializeOrHold(inputs, ruleset)
|
self:initializeOrHold(inputs, ruleset)
|
||||||
end
|
end
|
||||||
elseif self.lcd > 0 then
|
elseif self.lcd > 0 then
|
||||||
self.lcd = self.lcd - 1
|
self.lcd = self.lcd - 1
|
||||||
|
if ruleset.are_cancel and
|
||||||
|
(self.move == "none" and not self.prev_inputs["up"] and
|
||||||
|
not self.prev_inputs["rotate_left"] and not self.prev_inputs["rotate_left2"] and
|
||||||
|
not self.prev_inputs["rotate_right"] and not self.prev_inputs["rotate_right2"] and
|
||||||
|
not self.prev_inputs["rotate_180"]) and
|
||||||
|
(inputs["left"] or inputs["right"] or inputs["up"] or
|
||||||
|
inputs["rotate_left"] or inputs["rotate_left2"] or
|
||||||
|
inputs["rotate_right"] or inputs["rotate_right2"] or
|
||||||
|
inputs["rotate_180"]) then
|
||||||
|
self.lcd = 0
|
||||||
|
self.are = 0
|
||||||
|
end
|
||||||
if self.lcd == 0 then
|
if self.lcd == 0 then
|
||||||
self.grid:clearClearedRows()
|
self.grid:clearClearedRows()
|
||||||
|
playSE("fall")
|
||||||
if self.are == 0 then
|
if self.are == 0 then
|
||||||
self:initializeOrHold(inputs, ruleset)
|
self:initializeOrHold(inputs, ruleset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif self.are > 0 then
|
elseif self.are > 0 then
|
||||||
self.are = self.are - 1
|
self.are = self.are - 1
|
||||||
|
if ruleset.are_cancel and
|
||||||
|
(self.move == "none" and not self.prev_inputs["up"] and
|
||||||
|
not self.prev_inputs["rotate_left"] and not self.prev_inputs["rotate_left2"] and
|
||||||
|
not self.prev_inputs["rotate_right"] and not self.prev_inputs["rotate_right2"] and
|
||||||
|
not self.prev_inputs["rotate_180"]) and
|
||||||
|
(inputs["left"] or inputs["right"] or inputs["up"] or
|
||||||
|
inputs["rotate_left"] or inputs["rotate_left2"] or
|
||||||
|
inputs["rotate_right"] or inputs["rotate_right2"] or
|
||||||
|
inputs["rotate_180"]) then
|
||||||
|
self.are = 0
|
||||||
|
end
|
||||||
if self.are == 0 then
|
if self.are == 0 then
|
||||||
self:initializeOrHold(inputs, ruleset)
|
self:initializeOrHold(inputs, ruleset)
|
||||||
end
|
end
|
||||||
@@ -261,8 +370,8 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:initializeOrHold(inputs, ruleset)
|
function GameMode:initializeOrHold(inputs, ruleset)
|
||||||
if self.enable_hold and inputs["hold"] == true then
|
if self.ihs and self.enable_hold and inputs["hold"] == true then
|
||||||
self:hold(inputs, ruleset)
|
self:hold(inputs, ruleset, true)
|
||||||
else
|
else
|
||||||
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
|
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
|
||||||
end
|
end
|
||||||
@@ -273,7 +382,7 @@ function GameMode:initializeOrHold(inputs, ruleset)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:hold(inputs, ruleset)
|
function GameMode:hold(inputs, ruleset, ihs)
|
||||||
local data = copy(self.hold_queue)
|
local data = copy(self.hold_queue)
|
||||||
if self.piece == nil then
|
if self.piece == nil then
|
||||||
self.hold_queue = self.next_queue[1]
|
self.hold_queue = self.next_queue[1]
|
||||||
@@ -292,6 +401,8 @@ function GameMode:hold(inputs, ruleset)
|
|||||||
self:initializeNextPiece(inputs, ruleset, data, false)
|
self:initializeNextPiece(inputs, ruleset, data, false)
|
||||||
end
|
end
|
||||||
self.held = true
|
self.held = true
|
||||||
|
if ihs then playSE("ihs")
|
||||||
|
else playSE("hold") end
|
||||||
self:onHold()
|
self:onHold()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -301,7 +412,8 @@ 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.big_mode
|
self.lock_drop, self.lock_hard_drop, self.big_mode,
|
||||||
|
self.irs
|
||||||
)
|
)
|
||||||
if self.lock_drop then
|
if self.lock_drop then
|
||||||
self.drop_locked = true
|
self.drop_locked = true
|
||||||
@@ -313,7 +425,7 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
|
|||||||
table.remove(self.next_queue, 1)
|
table.remove(self.next_queue, 1)
|
||||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||||
end
|
end
|
||||||
self:playNextSound()
|
if config.gamesettings.next_se == 1 then self:playNextSound() end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:playNextSound()
|
function GameMode:playNextSound()
|
||||||
@@ -346,11 +458,12 @@ function GameMode:drawGhostPiece(ruleset)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:drawNextQueue(ruleset)
|
function GameMode:drawNextQueue(ruleset)
|
||||||
|
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||||
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][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for i = 1, self.next_queue_length do
|
for i = 1, self.next_queue_length do
|
||||||
@@ -364,8 +477,9 @@ function GameMode:drawNextQueue(ruleset)
|
|||||||
drawPiece(next_piece, skin, ruleset.block_offsets[next_piece][rotation], -16+i*80, -32)
|
drawPiece(next_piece, skin, ruleset.block_offsets[next_piece][rotation], -16+i*80, -32)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.hold_queue ~= nil then
|
if self.hold_queue ~= nil and self.enable_hold then
|
||||||
self:setHoldOpacity()
|
local hold_color = self.held and 0.6 or 1
|
||||||
|
self:setHoldOpacity(1, hold_color)
|
||||||
drawPiece(
|
drawPiece(
|
||||||
self.hold_queue.shape,
|
self.hold_queue.shape,
|
||||||
self.hold_queue.skin,
|
self.hold_queue.skin,
|
||||||
@@ -376,8 +490,16 @@ function GameMode:drawNextQueue(ruleset)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:setNextOpacity(i) love.graphics.setColor(1, 1, 1, 1) end
|
function GameMode:setNextOpacity(i, j)
|
||||||
function GameMode:setHoldOpacity() love.graphics.setColor(1, 1, 1, 1) end
|
i = i ~= nil and i or 1
|
||||||
|
j = j ~= nil and j or 1
|
||||||
|
love.graphics.setColor(j, j, j, i)
|
||||||
|
end
|
||||||
|
function GameMode:setHoldOpacity(i, j)
|
||||||
|
i = i ~= nil and i or 1
|
||||||
|
j = j ~= nil and j or 1
|
||||||
|
love.graphics.setColor(j, j, j, i)
|
||||||
|
end
|
||||||
|
|
||||||
function GameMode:drawScoringInfo()
|
function GameMode:drawScoringInfo()
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
@@ -392,7 +514,7 @@ 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
|
self.drop_bonus
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -412,6 +534,10 @@ function GameMode:drawSectionTimes(current_section)
|
|||||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
|
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function GameMode:sectionColourFunction(section)
|
||||||
|
return { 1, 1, 1, 1 }
|
||||||
|
end
|
||||||
|
|
||||||
function GameMode:drawSectionTimesWithSecondary(current_section)
|
function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||||
local section_x = 530
|
local section_x = 530
|
||||||
local section_secondary_x = 440
|
local section_secondary_x = 440
|
||||||
@@ -423,9 +549,11 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for section, time in pairs(self.secondary_section_times) do
|
for section, time in pairs(self.secondary_section_times) do
|
||||||
|
love.graphics.setColor(self:sectionColourFunction(section))
|
||||||
if section > 0 then
|
if section > 0 then
|
||||||
love.graphics.printf(formatTime(time), section_secondary_x, 40 + 20 * section, 90, "left")
|
love.graphics.printf(formatTime(time), section_secondary_x, 40 + 20 * section, 90, "left")
|
||||||
end
|
end
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local current_x
|
local current_x
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
require 'funcs'
|
|
||||||
|
|
||||||
local GameMode = require 'tetris.modes.gamemode'
|
|
||||||
local Piece = require 'tetris.components.piece'
|
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
|
||||||
|
|
||||||
local IntervalTrainingGame = GameMode:extend()
|
|
||||||
|
|
||||||
IntervalTrainingGame.name = "Interval Training"
|
|
||||||
IntervalTrainingGame.hash = "IntervalTraining"
|
|
||||||
IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function IntervalTrainingGame:new()
|
|
||||||
IntervalTrainingGame.super:new()
|
|
||||||
self.roll_frames = 0
|
|
||||||
self.combo = 1
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
|
||||||
self.section_time_limit = 1800
|
|
||||||
self.section_start_time = 0
|
|
||||||
self.section_times = { [0] = 0 }
|
|
||||||
self.lock_drop = true
|
|
||||||
self.enable_hold = true
|
|
||||||
self.next_queue_length = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getARE()
|
|
||||||
return 4
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getLineARE()
|
|
||||||
return 4
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getDasLimit()
|
|
||||||
return 6
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getLineClearDelay()
|
|
||||||
return 6
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getLockDelay()
|
|
||||||
return 15
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getGravity()
|
|
||||||
return 20
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getSection()
|
|
||||||
return math.floor(level / 100) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:advanceOneFrame()
|
|
||||||
if self.clear then
|
|
||||||
self.roll_frames = self.roll_frames + 1
|
|
||||||
if self.roll_frames > 2968 then
|
|
||||||
self.completed = true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
elseif self.ready_frames == 0 then
|
|
||||||
self.frames = self.frames + 1
|
|
||||||
if self:getSectionTime() >= self.section_time_limit then
|
|
||||||
self.game_over = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:onPieceEnter()
|
|
||||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
|
||||||
self.level = self.level + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:onLineClear(cleared_row_count)
|
|
||||||
if not self.clear then
|
|
||||||
local new_level = self.level + cleared_row_count
|
|
||||||
self:updateSectionTimes(self.level, new_level)
|
|
||||||
self.level = math.min(new_level, 999)
|
|
||||||
if self.level == 999 then
|
|
||||||
self.clear = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getSectionTime()
|
|
||||||
return self.frames - self.section_start_time
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:updateSectionTimes(old_level, new_level)
|
|
||||||
if math.floor(old_level / 100) < math.floor(new_level / 100) then
|
|
||||||
-- record new section
|
|
||||||
table.insert(self.section_times, self:getSectionTime())
|
|
||||||
self.section_start_time = self.frames
|
|
||||||
else
|
|
||||||
self.level = math.min(new_level, 999)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:drawGrid(ruleset)
|
|
||||||
self.grid:draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getHighscoreData()
|
|
||||||
return {
|
|
||||||
level = self.level,
|
|
||||||
frames = self.frames,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:drawScoringInfo()
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
|
||||||
love.graphics.print(
|
|
||||||
self.das.direction .. " " ..
|
|
||||||
self.das.frames .. " " ..
|
|
||||||
st(self.prev_inputs)
|
|
||||||
)
|
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
|
||||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
|
||||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
|
||||||
|
|
||||||
local current_section = math.floor(self.level / 100) + 1
|
|
||||||
self:drawSectionTimesWithSplits(current_section)
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
|
||||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
|
||||||
|
|
||||||
-- draw time left, flash red if necessary
|
|
||||||
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
|
|
||||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
|
||||||
end
|
|
||||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getSectionEndLevel()
|
|
||||||
if self.level >= 900 then return 999
|
|
||||||
else return math.floor(self.level / 100 + 1) * 100 end
|
|
||||||
end
|
|
||||||
|
|
||||||
function IntervalTrainingGame:getBackground()
|
|
||||||
return math.floor(self.level / 100)
|
|
||||||
end
|
|
||||||
|
|
||||||
return IntervalTrainingGame
|
|
||||||
@@ -36,6 +36,8 @@ function Marathon2020Game:new()
|
|||||||
self.grade_points = 0
|
self.grade_points = 0
|
||||||
self.grade_point_decay_counter = 0
|
self.grade_point_decay_counter = 0
|
||||||
self.max_grade_points = 0
|
self.max_grade_points = 0
|
||||||
|
|
||||||
|
self.cool_timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:getARE()
|
function Marathon2020Game:getARE()
|
||||||
@@ -142,6 +144,7 @@ function Marathon2020Game:advanceOneFrame()
|
|||||||
if self.roll_frames < 0 then
|
if self.roll_frames < 0 then
|
||||||
return false
|
return false
|
||||||
elseif self.roll_frames > 4000 then
|
elseif self.roll_frames > 4000 then
|
||||||
|
if self.grade >= 30 and self.section_cool_count >= 20 then self.grade = 31 end
|
||||||
self.completed = true
|
self.completed = true
|
||||||
end
|
end
|
||||||
elseif self.ready_frames == 0 then
|
elseif self.ready_frames == 0 then
|
||||||
@@ -151,11 +154,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 }
|
||||||
@@ -246,6 +249,7 @@ function Marathon2020Game:updateGrade(cleared_lines)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:getTotalGrade()
|
function Marathon2020Game:getTotalGrade()
|
||||||
|
if self.grade + self.section_cool_count > 50 then return "GM" end
|
||||||
return self.grade + self.section_cool_count
|
return self.grade + self.section_cool_count
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,11 +288,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)
|
||||||
@@ -323,10 +327,11 @@ function Marathon2020Game:checkClear(level)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||||
function sectionCool()
|
function sectionCool(section)
|
||||||
self.section_cool_count = self.section_cool_count + 1
|
self.section_cool_count = self.section_cool_count + 1
|
||||||
self.delay_level = math.min(20, self.delay_level + 1)
|
self.delay_level = math.min(20, self.delay_level + 1)
|
||||||
table.insert(self.section_status, "cool")
|
if section < 10 then table.insert(self.section_status, "cool") end
|
||||||
|
self.cool_timer = 300
|
||||||
end
|
end
|
||||||
|
|
||||||
local section = getSectionForLevel(old_level)
|
local section = getSectionForLevel(old_level)
|
||||||
@@ -343,7 +348,7 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
|||||||
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 > 4 then self.delay_level = math.min(20, self.delay_level + 1) end
|
if section > 5 then self.delay_level = math.min(20, self.delay_level + 1) end
|
||||||
self:checkTorikan(section)
|
self:checkTorikan(section)
|
||||||
self:checkClear(new_level)
|
self:checkClear(new_level)
|
||||||
|
|
||||||
@@ -352,11 +357,11 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
|||||||
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
|
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
|
||||||
self.secondary_section_times[section] < cool_cutoffs[section]
|
self.secondary_section_times[section] < cool_cutoffs[section]
|
||||||
) then
|
) then
|
||||||
sectionCool()
|
sectionCool(section)
|
||||||
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 <= 19 and self.secondary_section_times[section] < cool_cutoffs[section] then
|
elseif section <= 19 and self.secondary_section_times[section] < cool_cutoffs[section] then
|
||||||
sectionCool()
|
sectionCool(section)
|
||||||
else
|
else
|
||||||
table.insert(self.section_status, "none")
|
table.insert(self.section_status, "none")
|
||||||
end
|
end
|
||||||
@@ -389,7 +394,6 @@ Marathon2020Game.mRollOpacityFunction = function(age)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:qualifiesForMRoll()
|
function Marathon2020Game:qualifiesForMRoll()
|
||||||
return false -- until I actually have grading working
|
|
||||||
--[[
|
--[[
|
||||||
|
|
||||||
GM-roll requirements
|
GM-roll requirements
|
||||||
@@ -400,6 +404,8 @@ You qualify for the GM roll if you:
|
|||||||
- in less than 13:30.00 total.
|
- in less than 13:30.00 total.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
|
return self.level >= 2020 and self:getTotalGrade() == 50 and self.frames <= frameTime(13,30)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Marathon2020Game:drawGrid()
|
function Marathon2020Game:drawGrid()
|
||||||
@@ -417,6 +423,14 @@ function Marathon2020Game:drawGrid()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Marathon2020Game:sectionColourFunction(section)
|
||||||
|
if self.section_status[section] == "cool" then
|
||||||
|
return { 0, 1, 0, 1 }
|
||||||
|
else
|
||||||
|
return { 1, 1, 1, 1 }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Marathon2020Game:drawScoringInfo()
|
function Marathon2020Game:drawScoringInfo()
|
||||||
Marathon2020Game.super.drawScoringInfo(self)
|
Marathon2020Game.super.drawScoringInfo(self)
|
||||||
|
|
||||||
@@ -430,6 +444,11 @@ function Marathon2020Game:drawScoringInfo()
|
|||||||
|
|
||||||
self:drawSectionTimesWithSecondary(current_section)
|
self:drawSectionTimesWithSecondary(current_section)
|
||||||
|
|
||||||
|
if (self.cool_timer > 0) then
|
||||||
|
love.graphics.printf("COOL!!", 64, 400, 160, "center")
|
||||||
|
self.cool_timer = self.cool_timer - 1
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
|
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
|
||||||
love.graphics.printf(self.grade_points, text_x, 220, 90, "left")
|
love.graphics.printf(self.grade_points, text_x, 220, 90, "left")
|
||||||
|
|||||||
@@ -18,11 +18,17 @@ function MarathonA1Game:new()
|
|||||||
|
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
|
self.bravos = 0
|
||||||
self.gm_conditions = {
|
self.gm_conditions = {
|
||||||
level300 = false,
|
level300 = false,
|
||||||
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()
|
||||||
|
|
||||||
@@ -131,40 +137,44 @@ function MarathonA1Game:onLineClear(cleared_row_count)
|
|||||||
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||||
if self.level == 999 then
|
if new_level == 999 then
|
||||||
self.clear = true
|
self.clear = true
|
||||||
else
|
|
||||||
self.level = new_level
|
|
||||||
end
|
end
|
||||||
|
self.level = new_level
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
|
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
|
if self.grid:checkForBravo(cleared_lines) then
|
||||||
|
self.bravo = 4
|
||||||
|
self.bravos = self.bravos + 1
|
||||||
|
else self.bravo = 1 end
|
||||||
if cleared_lines > 0 then
|
if cleared_lines > 0 then
|
||||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
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 * self.combo
|
cleared_lines * self.combo * self.bravo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
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["level999"] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -184,17 +194,23 @@ 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
|
||||||
|
|
||||||
|
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "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")
|
||||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
|
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||||
else
|
else
|
||||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||||
@@ -202,6 +218,10 @@ 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
|
||||||
|
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "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")
|
||||||
|
|||||||
@@ -27,9 +27,14 @@ 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.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 = false
|
self.lock_drop = false
|
||||||
|
self.lock_hard_drop = false
|
||||||
self.enable_hold = false
|
self.enable_hold = false
|
||||||
self.next_queue_length = 1
|
self.next_queue_length = 1
|
||||||
end
|
end
|
||||||
@@ -103,6 +108,7 @@ end
|
|||||||
function MarathonA2Game:advanceOneFrame()
|
function MarathonA2Game: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 return false end
|
||||||
if self.roll_frames > 3694 then
|
if self.roll_frames > 3694 then
|
||||||
self.completed = true
|
self.completed = true
|
||||||
if self.grade == 32 then
|
if self.grade == 32 then
|
||||||
@@ -121,32 +127,33 @@ function MarathonA2Game:onPieceEnter()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
|
self:updateGrade(cleared_lines)
|
||||||
|
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||||
|
if cleared_lines > 0 then
|
||||||
|
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||||
|
self.score = self.score + (
|
||||||
|
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||||
|
cleared_lines * self.combo * self.bravo
|
||||||
|
)
|
||||||
|
else
|
||||||
|
self.combo = 1
|
||||||
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
else self.lines = self.lines + cleared_lines end
|
||||||
|
end
|
||||||
|
|
||||||
function MarathonA2Game:onLineClear(cleared_row_count)
|
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||||
self:updateSectionTimes(self.level, self.level + cleared_row_count)
|
|
||||||
self.level = math.min(self.level + cleared_row_count, 999)
|
self.level = math.min(self.level + cleared_row_count, 999)
|
||||||
if self.level == 999 and not self.clear then
|
if self.level == 999 and not self.clear then
|
||||||
self.clear = true
|
self.clear = true
|
||||||
if self:qualifiesForMRoll() then
|
|
||||||
self.grade = 32
|
|
||||||
end
|
|
||||||
self.grid:clear()
|
self.grid:clear()
|
||||||
|
if self:qualifiesForMRoll() then self.grade = 32 end
|
||||||
self.roll_frames = -150
|
self.roll_frames = -150
|
||||||
end
|
end
|
||||||
end
|
self.lock_drop = self.level >= 900
|
||||||
|
self.lock_hard_drop = self.level >= 900
|
||||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
|
||||||
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
|
end
|
||||||
|
|
||||||
function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
||||||
@@ -246,7 +253,7 @@ function MarathonA2Game:updateGrade(cleared_lines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
|
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 0 }
|
||||||
|
|
||||||
function MarathonA2Game:qualifiesForMRoll()
|
function MarathonA2Game:qualifiesForMRoll()
|
||||||
if not self.clear then return false end
|
if not self.clear then return false end
|
||||||
@@ -260,7 +267,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 +280,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 < 31 or self.frames > frameTime(8,45) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -303,12 +310,12 @@ MarathonA2Game.mRollOpacityFunction = function(age)
|
|||||||
else return 1 - age / 4 end
|
else return 1 - age / 4 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA2Game:drawGrid(ruleset)
|
function MarathonA2Game:drawGrid()
|
||||||
if self.clear and not (self.completed or self.game_over) then
|
if self.clear and not (self.completed or self.game_over) then
|
||||||
if self:qualifiesForMRoll() then
|
if self:qualifiesForMRoll() then
|
||||||
self.grid:drawInvisible(self.mRollOpacityFunction)
|
self.grid:drawInvisible(self.mRollOpacityFunction, nil, false)
|
||||||
else
|
else
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
@@ -325,18 +332,35 @@ 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)
|
||||||
|
if self.clear then
|
||||||
|
if self:qualifiesForMRoll() then
|
||||||
|
if self.lines >= 32 and self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
else love.graphics.setColor(0, 1, 0, 1) end
|
||||||
|
else
|
||||||
|
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
else love.graphics.setColor(0, 1, 0, 1) end
|
||||||
|
end
|
||||||
|
end
|
||||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
love.graphics.printf(self.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")
|
||||||
|
|||||||
@@ -29,13 +29,25 @@ function MarathonA3Game:new()
|
|||||||
self.section_start_time = 0
|
self.section_start_time = 0
|
||||||
self.section_70_times = { [0] = 0 }
|
self.section_70_times = { [0] = 0 }
|
||||||
self.section_times = { [0] = 0 }
|
self.section_times = { [0] = 0 }
|
||||||
|
self.section_cool = false
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
|
self.torikan_passed = false
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA3Game:getARE()
|
function MarathonA3Game:getARE()
|
||||||
@@ -140,6 +152,7 @@ function MarathonA3Game:onPieceEnter()
|
|||||||
self:updateSectionTimes(self.level, self.level + 1)
|
self:updateSectionTimes(self.level, self.level + 1)
|
||||||
self.level = self.level + 1
|
self.level = self.level + 1
|
||||||
self.speed_level = self.speed_level + 1
|
self.speed_level = self.speed_level + 1
|
||||||
|
self.torikan_passed = self.level >= 500 and true or false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -157,16 +170,20 @@ function MarathonA3Game:onLineClear(cleared_row_count)
|
|||||||
self.grid:clear()
|
self.grid:clear()
|
||||||
self.roll_frames = -150
|
self.roll_frames = -150
|
||||||
end
|
end
|
||||||
|
if not self.torikan_passed and self.level >= 500 and self.frames >= 25200 then
|
||||||
|
self.level = 500
|
||||||
|
self.game_over = true
|
||||||
|
end
|
||||||
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)
|
||||||
@@ -179,43 +196,54 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
|||||||
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
|
||||||
|
|
||||||
|
self.speed_level = self.section_cool and self.speed_level + 100 or self.speed_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")
|
||||||
elseif section <= 9 and self.section_status[section - 1] == "cool" and
|
self.coolregret_message = "REGRET!!"
|
||||||
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then
|
self.coolregret_timer = 300
|
||||||
|
elseif self.section_cool 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
|
|
||||||
table.insert(self.section_status, "cool")
|
|
||||||
elseif self.section_status[section - 1] == "cool" then
|
|
||||||
table.insert(self.section_status, "none")
|
|
||||||
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
|
|
||||||
self.section_cool_grade = self.section_cool_grade + 1
|
|
||||||
self.speed_level = self.speed_level + 100
|
|
||||||
table.insert(self.section_status, "cool")
|
table.insert(self.section_status, "cool")
|
||||||
else
|
else
|
||||||
table.insert(self.section_status, "none")
|
table.insert(self.section_status, "none")
|
||||||
end
|
end
|
||||||
elseif section <= 9 and old_level % 100 < 70 and new_level % 100 >= 70 then
|
|
||||||
|
self.section_cool = false
|
||||||
|
elseif old_level % 100 < 70 and new_level % 100 >= 70 then
|
||||||
-- record section 70 time
|
-- record section 70 time
|
||||||
section_70_time = self.frames - self.section_start_time
|
section_70_time = self.frames - self.section_start_time
|
||||||
table.insert(self.section_70_times, section_70_time)
|
table.insert(self.section_70_times, section_70_time)
|
||||||
|
|
||||||
|
if section <= 9 and self.section_status[section - 1] == "cool" and
|
||||||
|
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then
|
||||||
|
self.section_cool = true
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
|
elseif self.section_status[section - 1] == "cool" then self.section_cool = false
|
||||||
|
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
|
||||||
|
self.section_cool = true
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
|
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
self:updateGrade(cleared_lines)
|
self:updateGrade(cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local grade_point_bonuses = {
|
local grade_point_bonuses = {
|
||||||
@@ -334,6 +362,8 @@ function MarathonA3Game:getLetterGrade()
|
|||||||
return "M" .. tostring(grade - 17)
|
return "M" .. tostring(grade - 17)
|
||||||
elseif grade < 32 then
|
elseif grade < 32 then
|
||||||
return master_grades[grade - 26]
|
return master_grades[grade - 26]
|
||||||
|
elseif grade >= 32 and self.roll_frames < 3238 then
|
||||||
|
return "MM"
|
||||||
else
|
else
|
||||||
return "GM"
|
return "GM"
|
||||||
end
|
end
|
||||||
@@ -372,12 +402,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
|
||||||
@@ -404,13 +438,24 @@ function MarathonA3Game:drawScoringInfo()
|
|||||||
current_x = section_70_x
|
current_x = section_70_x
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
if not self.clear then love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "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
|
||||||
|
|
||||||
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")
|
||||||
|
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
elseif self.level >= 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
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")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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 MarathonAX4Game = GameMode:extend()
|
local MarathonAX4Game = GameMode:extend()
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ function MarathonAX4Game:new()
|
|||||||
MarathonAX4Game.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
|
||||||
@@ -24,6 +24,7 @@ function MarathonAX4Game:new()
|
|||||||
self.section_clear = false
|
self.section_clear = false
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_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
|
||||||
end
|
end
|
||||||
@@ -98,6 +99,7 @@ function MarathonAX4Game:onLineClear(cleared_row_count)
|
|||||||
self:updateSectionTimes(self.lines, new_lines)
|
self:updateSectionTimes(self.lines, new_lines)
|
||||||
self.lines = math.min(new_lines, 150)
|
self.lines = math.min(new_lines, 150)
|
||||||
if self.lines == 150 then
|
if self.lines == 150 then
|
||||||
|
self.grid:clear()
|
||||||
self.clear = true
|
self.clear = true
|
||||||
self.roll_frames = -150
|
self.roll_frames = -150
|
||||||
end
|
end
|
||||||
@@ -141,10 +143,10 @@ function MarathonAX4Game: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")
|
if self.lines < 150 then love.graphics.printf("TIME LEFT", 240, 250, 80, "left") end
|
||||||
love.graphics.printf("LINES", 240, 320, 40, "left")
|
love.graphics.printf("LINES", 240, 320, 40, "left")
|
||||||
|
|
||||||
local current_section = math.floor(self.lines / 10) + 1
|
local current_section = math.floor(self.lines / 10) + 1
|
||||||
@@ -156,10 +158,10 @@ function MarathonAX4Game: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")
|
if self.lines < 150 then love.graphics.printf(formatTime(time_left), 240, 270, 160, "left") end
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
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 .. " " ..
|
|
||||||
st(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
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
require 'funcs'
|
|
||||||
|
|
||||||
local GameMode = require 'tetris.modes.gamemode'
|
|
||||||
local Piece = require 'tetris.components.piece'
|
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
|
||||||
|
|
||||||
local PacerTest = GameMode:extend()
|
|
||||||
|
|
||||||
PacerTest.name = "TetrisGram™ Pacer Test"
|
|
||||||
PacerTest.hash = "PacerTest"
|
|
||||||
PacerTest.tagline = ""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function getLevelFrames(level)
|
|
||||||
if level == 1 then return 72 * 60 / 8.0
|
|
||||||
else return 72 * 60 / (8 + level * 0.5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local level_end_sections = {
|
|
||||||
7, 15, 23, 32, 41, 51, 61, 72, 83, 94,
|
|
||||||
106, 118, 131, 144, 157, 171, 185, 200,
|
|
||||||
215, 231, 247
|
|
||||||
}
|
|
||||||
|
|
||||||
function PacerTest:new()
|
|
||||||
PacerTest.super:new()
|
|
||||||
|
|
||||||
self.ready_frames = 2430
|
|
||||||
self.clear_frames = 0
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
|
||||||
|
|
||||||
self.level = 1
|
|
||||||
self.section = 0
|
|
||||||
self.level_frames = 0
|
|
||||||
|
|
||||||
self.section_lines = 0
|
|
||||||
self.section_clear = false
|
|
||||||
self.strikes = 0
|
|
||||||
|
|
||||||
self.lock_drop = true
|
|
||||||
self.lock_hard_drop = true
|
|
||||||
self.enable_hold = true
|
|
||||||
self.instant_hard_drop = true
|
|
||||||
self.instant_soft_drop = false
|
|
||||||
self.next_queue_length = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:initialize(ruleset)
|
|
||||||
for i = 1, 30 do
|
|
||||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
|
||||||
end
|
|
||||||
self.level_frames = getLevelFrames(1)
|
|
||||||
switchBGM("pacer_test")
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getARE()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getLineARE()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getDasLimit()
|
|
||||||
return 8
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getLineClearDelay()
|
|
||||||
return 6
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getLockDelay()
|
|
||||||
return 30
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getGravity()
|
|
||||||
return 1/64
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getSection()
|
|
||||||
return math.floor(level / 100) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:advanceOneFrame()
|
|
||||||
if self.clear then
|
|
||||||
self.clear_frames = self.clear_frames + 1
|
|
||||||
if self.clear_frames > 600 then
|
|
||||||
self.completed = true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
elseif self.ready_frames == 0 then
|
|
||||||
self.frames = self.frames + 1
|
|
||||||
self.level_frames = self.level_frames - 1
|
|
||||||
if self.level_frames <= 0 then
|
|
||||||
self:checkSectionStatus()
|
|
||||||
self.section = self.section + 1
|
|
||||||
if self.section >= level_end_sections[self.level] then
|
|
||||||
self.level = self.level + 1
|
|
||||||
end
|
|
||||||
self.level_frames = self.level_frames + getLevelFrames(self.level)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:checkSectionStatus()
|
|
||||||
if self.section_clear then
|
|
||||||
self.strikes = 0
|
|
||||||
self.section_clear = false
|
|
||||||
else
|
|
||||||
self.strikes = self.strikes + 1
|
|
||||||
if self.strikes >= 2 then
|
|
||||||
self.game_over = true
|
|
||||||
fadeoutBGM(2.5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.section_lines = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:onLineClear(cleared_row_count)
|
|
||||||
self.section_lines = self.section_lines + cleared_row_count
|
|
||||||
if self.section_lines >= 3 then
|
|
||||||
self.section_clear = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:drawGrid(ruleset)
|
|
||||||
self.grid:draw()
|
|
||||||
if self.piece ~= nil then
|
|
||||||
self:drawGhostPiece(ruleset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getHighscoreData()
|
|
||||||
return {
|
|
||||||
level = self.level,
|
|
||||||
frames = self.frames,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:drawScoringInfo()
|
|
||||||
PacerTest.super.drawScoringInfo(self)
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
|
|
||||||
local text_x = config["side_next"] and 320 or 240
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
|
||||||
love.graphics.printf("LINES", text_x, 224, 70, "left")
|
|
||||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
|
||||||
|
|
||||||
for i = 1, math.min(self.strikes, 3) do
|
|
||||||
love.graphics.draw(misc_graphics["strike"], text_x + (i - 1) * 30, 280)
|
|
||||||
end
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
|
||||||
love.graphics.printf(self.section_lines .. "/3", text_x, 244, 40, "left")
|
|
||||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
|
||||||
love.graphics.printf(self.section, text_x, 370, 40, "right")
|
|
||||||
end
|
|
||||||
|
|
||||||
function PacerTest:getBackground()
|
|
||||||
return math.min(self.level - 1, 19)
|
|
||||||
end
|
|
||||||
|
|
||||||
return PacerTest
|
|
||||||
@@ -15,8 +15,15 @@ function PhantomManiaGame:new()
|
|||||||
PhantomManiaGame.super:new()
|
PhantomManiaGame.super:new()
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_drop = true
|
||||||
|
self.lock_hard_drop = true
|
||||||
self.next_queue_length = 1
|
self.next_queue_length = 1
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
@@ -61,15 +68,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
|
||||||
@@ -111,17 +118,18 @@ function PhantomManiaGame:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
|
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
PhantomManiaGame.rollOpacityFunction = function(age)
|
PhantomManiaGame.rollOpacityFunction = function(age)
|
||||||
@@ -131,7 +139,7 @@ end
|
|||||||
|
|
||||||
function PhantomManiaGame:drawGrid()
|
function PhantomManiaGame:drawGrid()
|
||||||
if not (self.game_over or self.clear) then
|
if not (self.game_over or self.clear) then
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
|
||||||
else
|
else
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
@@ -161,12 +169,16 @@ function PhantomManiaGame:drawScoringInfo()
|
|||||||
local text_x = config["side_next"] and 320 or 240
|
local text_x = config["side_next"] and 320 or 240
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
love.graphics.setFont(font_3x5_2)
|
||||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||||
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(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
|
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end
|
||||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||||
if self.clear then
|
if self.clear then
|
||||||
@@ -175,6 +187,9 @@ function PhantomManiaGame:drawScoringInfo()
|
|||||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if sg >= 5 then
|
||||||
|
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomManiaGame:getSectionEndLevel()
|
function PhantomManiaGame:getSectionEndLevel()
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make
|
|||||||
|
|
||||||
function PhantomMania2Game:new()
|
function PhantomMania2Game:new()
|
||||||
PhantomMania2Game.super:new()
|
PhantomMania2Game.super:new()
|
||||||
self.level = 0
|
|
||||||
self.grade = 0
|
self.grade = 0
|
||||||
self.garbage = 0
|
self.garbage = 0
|
||||||
self.clear = false
|
self.clear = false
|
||||||
@@ -27,11 +26,21 @@ function PhantomMania2Game:new()
|
|||||||
self.queue_age = 0
|
self.queue_age = 0
|
||||||
self.roll_points = 0
|
self.roll_points = 0
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||||
|
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_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 = ""
|
||||||
|
self.coolregret_timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:getARE()
|
function PhantomMania2Game:getARE()
|
||||||
@@ -86,19 +95,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,45) 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
|
||||||
@@ -116,6 +125,8 @@ function PhantomMania2Game:advanceOneFrame()
|
|||||||
return false
|
return false
|
||||||
elseif self.roll_frames > 3238 then
|
elseif self.roll_frames > 3238 then
|
||||||
switchBGM(nil)
|
switchBGM(nil)
|
||||||
|
self.roll_points = self.level >= 1300 and self.roll_points + 150 or self.roll_points
|
||||||
|
self.grade = self.grade + math.floor(self.roll_points / 100)
|
||||||
self.completed = true
|
self.completed = true
|
||||||
end
|
end
|
||||||
elseif self.ready_frames == 0 then
|
elseif self.ready_frames == 0 then
|
||||||
@@ -137,7 +148,8 @@ 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 = {2, 6, 15, 40}
|
local torikan_roll_points = {10, 20, 30, 100}
|
||||||
|
local big_roll_points = {10, 20, 100, 200}
|
||||||
|
|
||||||
function PhantomMania2Game:onLineClear(cleared_row_count)
|
function PhantomMania2Game:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
@@ -156,7 +168,8 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
self:advanceBottomRow(-cleared_row_count)
|
self:advanceBottomRow(-cleared_row_count)
|
||||||
else
|
else
|
||||||
self.roll_points = self.roll_points + cleared_row_points[cleared_row_count / 2]
|
if self.big_mode then self.roll_points = self.roll_points + big_roll_points[cleared_row_count / 2]
|
||||||
|
else self.roll_points = self.roll_points + torikan_roll_points[cleared_row_count] end
|
||||||
if self.roll_points >= 100 then
|
if self.roll_points >= 100 then
|
||||||
self.roll_points = self.roll_points - 100
|
self.roll_points = self.roll_points - 100
|
||||||
self.grade = self.grade + 1
|
self.grade = self.grade + 1
|
||||||
@@ -165,38 +178,41 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
|
function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
|
||||||
|
self.super:onPieceLock()
|
||||||
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:onHold()
|
function PhantomMania2Game:onHold()
|
||||||
|
self.super.onHold()
|
||||||
self.hold_age = 0
|
self.hold_age = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines)
|
function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
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)
|
||||||
@@ -207,8 +223,13 @@ function PhantomMania2Game:updateSectionTimes(old_level, new_level)
|
|||||||
self.section_start_time = self.frames
|
self.section_start_time = self.frames
|
||||||
if section_time <= cool_cutoffs[section] then
|
if section_time <= cool_cutoffs[section] then
|
||||||
self.grade = self.grade + 2
|
self.grade = self.grade + 2
|
||||||
|
self.coolregret_message = "COOL!!"
|
||||||
|
self.coolregret_timer = 300
|
||||||
elseif section_time <= regret_cutoffs[section] then
|
elseif section_time <= regret_cutoffs[section] 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
|
||||||
@@ -234,7 +255,7 @@ PhantomMania2Game.garbageOpacityFunction = function(age)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PhantomMania2Game:drawGrid()
|
function PhantomMania2Game:drawGrid()
|
||||||
if not (self.game_over or (self.clear and self.level < 1300)) then
|
if not (self.game_over) then
|
||||||
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
||||||
else
|
else
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
@@ -270,7 +291,7 @@ function PhantomMania2Game:setHoldOpacity()
|
|||||||
if self.level > 1000 and self.level < 1300 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)
|
self.super:setHoldOpacity(1, self.held and 0.6 or 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -285,6 +306,17 @@ function PhantomMania2Game: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
|
||||||
|
|
||||||
|
self:drawSectionTimesWithSplits(math.floor(self.level / 100) + 1)
|
||||||
|
|
||||||
|
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(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
||||||
@@ -295,6 +327,10 @@ function PhantomMania2Game: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 PhantomMania2Game:getBackground()
|
function PhantomMania2Game:getBackground()
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS su
|
|||||||
function PhantomManiaNGame:new()
|
function PhantomManiaNGame:new()
|
||||||
PhantomManiaNGame.super:new()
|
PhantomManiaNGame.super:new()
|
||||||
|
|
||||||
|
self.SGnames = {
|
||||||
|
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||||
|
"M10", "M11", "M12", "M13", "M14", "M15", "M16", "M17", "M18",
|
||||||
|
"GM"
|
||||||
|
}
|
||||||
|
|
||||||
self.next_queue_length = 3
|
self.next_queue_length = 3
|
||||||
self.enable_hold = true
|
self.enable_hold = true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
require 'funcs'
|
|
||||||
|
|
||||||
local GameMode = require 'tetris.modes.gamemode'
|
|
||||||
local Piece = require 'tetris.components.piece'
|
|
||||||
|
|
||||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
|
||||||
|
|
||||||
local Race40Game = GameMode:extend()
|
|
||||||
|
|
||||||
Race40Game.name = "Race 40"
|
|
||||||
Race40Game.hash = "Race40"
|
|
||||||
Race40Game.tagline = "How fast can you clear 40 lines?"
|
|
||||||
|
|
||||||
|
|
||||||
function Race40Game:new()
|
|
||||||
Race40Game.super:new()
|
|
||||||
|
|
||||||
self.lines = 0
|
|
||||||
self.line_goal = 40
|
|
||||||
self.pieces = 0
|
|
||||||
self.randomizer = History6RollsRandomizer()
|
|
||||||
|
|
||||||
self.roll_frames = 0
|
|
||||||
|
|
||||||
self.lock_drop = true
|
|
||||||
self.lock_hard_drop = true
|
|
||||||
self.instant_hard_drop = true
|
|
||||||
self.instant_soft_drop = false
|
|
||||||
self.enable_hold = true
|
|
||||||
self.next_queue_length = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getDropSpeed()
|
|
||||||
return 20
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getARR()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getARE()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getLineARE()
|
|
||||||
return self:getARE()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getDasLimit()
|
|
||||||
return 6
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getLineClearDelay()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getLockDelay()
|
|
||||||
return 15
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getGravity()
|
|
||||||
return 1/64
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:advanceOneFrame()
|
|
||||||
if self.clear then
|
|
||||||
self.roll_frames = self.roll_frames + 1
|
|
||||||
if self.roll_frames > 150 then
|
|
||||||
self.completed = true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
elseif self.ready_frames == 0 then
|
|
||||||
self.frames = self.frames + 1
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:onPieceLock()
|
|
||||||
self.pieces = self.pieces + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:onLineClear(cleared_row_count)
|
|
||||||
if not self.clear then
|
|
||||||
self.lines = self.lines + cleared_row_count
|
|
||||||
if self.lines >= self.line_goal then
|
|
||||||
self.clear = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:drawGrid(ruleset)
|
|
||||||
self.grid:draw()
|
|
||||||
if self.piece ~= nil then
|
|
||||||
self:drawGhostPiece(ruleset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getHighscoreData()
|
|
||||||
return {
|
|
||||||
level = self.level,
|
|
||||||
frames = self.frames,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:drawScoringInfo()
|
|
||||||
Race40Game.super.drawScoringInfo(self)
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
|
|
||||||
local text_x = config["side_next"] and 320 or 240
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_2)
|
|
||||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
|
||||||
love.graphics.printf("LINES", text_x, 320, 40, "left")
|
|
||||||
love.graphics.printf("line/min", text_x, 160, 80, "left")
|
|
||||||
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
|
||||||
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
|
|
||||||
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
|
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_4)
|
|
||||||
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Race40Game:getBackground()
|
|
||||||
return 2
|
|
||||||
end
|
|
||||||
|
|
||||||
return Race40Game
|
|
||||||
@@ -16,7 +16,6 @@ StrategyGame.tagline = "You have lots of time to think! Can you use it to place
|
|||||||
|
|
||||||
function StrategyGame:new()
|
function StrategyGame:new()
|
||||||
StrategyGame.super:new()
|
StrategyGame.super:new()
|
||||||
self.level = 0
|
|
||||||
self.clear = false
|
self.clear = false
|
||||||
self.completed = false
|
self.completed = false
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
@@ -84,7 +83,7 @@ function StrategyGame:advanceOneFrame()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function StrategyGame:onPieceEnter()
|
function StrategyGame:onPieceEnter()
|
||||||
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
|
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||||
self.level = self.level + 1
|
self.level = self.level + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -99,17 +98,18 @@ function StrategyGame:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function StrategyGame:updateScore(level, drop_bonus, cleared_lines)
|
function StrategyGame:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function StrategyGame:setNextOpacity(i)
|
function StrategyGame:setNextOpacity(i)
|
||||||
@@ -135,11 +135,11 @@ function StrategyGame:drawScoringInfo()
|
|||||||
|
|
||||||
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.level, text_x, 340, 50, "right")
|
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||||
if self.clear then
|
if self.clear then
|
||||||
love.graphics.printf(self.level, text_x, 370, 50, "right")
|
love.graphics.printf(self.level, text_x, 370, 40, "right")
|
||||||
else
|
else
|
||||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
love.graphics.printf(self.level < 900 and math.floor(self.level / 100 + 1) * 100 or 999, text_x, 370, 40, "right")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ function Survival2020Game:new()
|
|||||||
self.randomizer = History6RollsRandomizer()
|
self.randomizer = History6RollsRandomizer()
|
||||||
|
|
||||||
self.lock_drop = true
|
self.lock_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
|
||||||
end
|
end
|
||||||
@@ -102,15 +103,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
|
||||||
@@ -174,17 +175,18 @@ function Survival2020Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Survival2020Game:updateScore(level, drop_bonus, cleared_lines)
|
function Survival2020Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Survival2020Game:updateSectionTimes(old_level, new_level)
|
function Survival2020Game:updateSectionTimes(old_level, new_level)
|
||||||
@@ -193,7 +195,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
|
||||||
|
|||||||
@@ -19,12 +19,20 @@ function SurvivalA1Game:new()
|
|||||||
|
|
||||||
self.roll_frames = 0
|
self.roll_frames = 0
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
|
self.bravos = 0
|
||||||
|
|
||||||
self.gm_conditions = {
|
self.gm_conditions = {
|
||||||
level300 = false,
|
level300 = false,
|
||||||
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()
|
||||||
|
|
||||||
self.lock_drop = false
|
self.lock_drop = false
|
||||||
@@ -101,7 +109,7 @@ function SurvivalA1Game:onLineClear(cleared_row_count)
|
|||||||
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||||
if self.level == 999 then
|
if new_level == 999 then
|
||||||
self.clear = true
|
self.clear = true
|
||||||
else
|
else
|
||||||
self.level = new_level
|
self.level = new_level
|
||||||
@@ -110,31 +118,36 @@ function SurvivalA1Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
|
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
|
if self.grid:checkForBravo(cleared_lines) then
|
||||||
|
self.bravo = 4
|
||||||
|
self.bravos = self.bravos + 1
|
||||||
|
else self.bravo = 1 end
|
||||||
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.bravo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
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["level999"] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -151,17 +164,23 @@ 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
|
||||||
|
|
||||||
|
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "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")
|
||||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
|
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||||
else
|
else
|
||||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||||
@@ -169,6 +188,10 @@ 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
|
||||||
|
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "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")
|
||||||
|
|||||||
@@ -20,7 +20,14 @@ function SurvivalA2Game:new()
|
|||||||
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
|
||||||
|
self.lock_hard_drop = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:getARE()
|
function SurvivalA2Game:getARE()
|
||||||
@@ -62,7 +69,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
|
||||||
@@ -82,7 +89,7 @@ function SurvivalA2Game:advanceOneFrame()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:onPieceEnter()
|
function SurvivalA2Game:onPieceEnter()
|
||||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||||
self.level = self.level + 1
|
self.level = self.level + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -92,6 +99,9 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
|
|||||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||||
if self.level == 999 or self:hitTorikan(self.level, new_level) then
|
if self.level == 999 or self:hitTorikan(self.level, new_level) then
|
||||||
self.clear = true
|
self.clear = true
|
||||||
|
if self.level < 999 then
|
||||||
|
self.game_over = true
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self.level = new_level
|
self.level = new_level
|
||||||
end
|
end
|
||||||
@@ -99,22 +109,25 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines)
|
function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||||
|
if not self.clear then
|
||||||
|
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||||
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.bravo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA2Game:getLetterGrade()
|
function SurvivalA2Game:getLetterGrade()
|
||||||
if self.level >= 999 then return "GM"
|
if self.level >= 999 then return "GM"
|
||||||
elseif self.level >= 500 then return "M"
|
elseif self.level > 500 then return "M"
|
||||||
|
elseif self.level == 500 and not self.clear then return "M"
|
||||||
else return "" end
|
else return "" end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -132,18 +145,28 @@ 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")
|
if self:getLetterGrade() ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||||
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")
|
if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
elseif self.level >= 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||||
|
if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
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()
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ 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?"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function SurvivalA3Game:new()
|
function SurvivalA3Game:new()
|
||||||
SurvivalA3Game.super:new()
|
SurvivalA3Game.super:new()
|
||||||
self.level = 0
|
|
||||||
self.grade = 0
|
self.grade = 0
|
||||||
self.garbage = 0
|
self.garbage = 0
|
||||||
self.clear = false
|
self.clear = false
|
||||||
@@ -25,9 +22,27 @@ function SurvivalA3Game:new()
|
|||||||
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.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
|
||||||
|
|
||||||
|
function SurvivalA3Game:initialize(ruleset)
|
||||||
|
|
||||||
|
self.torikan_time = frameTime(2,28)
|
||||||
|
if ruleset.world then self.torikan_time = frameTime(3,03) end
|
||||||
|
self.super.initialize(self, ruleset)
|
||||||
|
-- ^ notice the . here instead of the :
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:getARE()
|
function SurvivalA3Game:getARE()
|
||||||
@@ -86,11 +101,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 > self.torikan_time 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 > self.torikan_time*2 then
|
||||||
self.level = 1000
|
self.level = 1000
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -123,20 +138,21 @@ function SurvivalA3Game: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}
|
|
||||||
|
|
||||||
function SurvivalA3Game:onLineClear(cleared_row_count)
|
function SurvivalA3Game:onLineClear(cleared_row_count)
|
||||||
if not self.clear then
|
if not self.clear then
|
||||||
local new_level = self.level + cleared_row_levels[cleared_row_count]
|
local new_level = self.level + cleared_row_levels[cleared_row_count]
|
||||||
self:updateSectionTimes(self.level, new_level)
|
self:updateSectionTimes(self.level, new_level)
|
||||||
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
||||||
|
self.clear = true
|
||||||
if new_level >= 1300 then
|
if new_level >= 1300 then
|
||||||
self.level = 1300
|
self.level = 1300
|
||||||
end
|
|
||||||
self.clear = true
|
|
||||||
self.grid:clear()
|
self.grid:clear()
|
||||||
self.big_mode = true
|
self.big_mode = true
|
||||||
self.roll_frames = -150
|
self.roll_frames = -150
|
||||||
|
else
|
||||||
|
self.game_over = true
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self.level = math.min(new_level, 1300)
|
self.level = math.min(new_level, 1300)
|
||||||
end
|
end
|
||||||
@@ -145,21 +161,23 @@ function SurvivalA3Game:onLineClear(cleared_row_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
|
function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
|
||||||
|
self.super:onPieceLock()
|
||||||
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
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)
|
||||||
|
if not self.clear then
|
||||||
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 * 2 - 1)
|
cleared_lines * self.combo
|
||||||
)
|
)
|
||||||
self.lines = self.lines + cleared_lines
|
|
||||||
self.combo = self.combo + cleared_lines - 1
|
|
||||||
else
|
else
|
||||||
self.drop_bonus = 0
|
|
||||||
self.combo = 1
|
self.combo = 1
|
||||||
end
|
end
|
||||||
|
self.drop_bonus = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function SurvivalA3Game:updateSectionTimes(old_level, new_level)
|
function SurvivalA3Game:updateSectionTimes(old_level, new_level)
|
||||||
@@ -168,8 +186,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
|
||||||
@@ -207,12 +228,24 @@ 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)
|
||||||
|
|
||||||
love.graphics.setFont(font_3x5_3)
|
love.graphics.setFont(font_3x5_3)
|
||||||
|
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||||
|
elseif self.level >= 1300 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||||
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||||
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
||||||
if self.clear then
|
if self.clear then
|
||||||
@@ -220,6 +253,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()
|
||||||
|
|||||||
@@ -2,17 +2,8 @@ local Randomizer = require 'tetris.randomizers.randomizer'
|
|||||||
|
|
||||||
local AlwaysRandomizer = Randomizer:extend()
|
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()
|
function AlwaysRandomizer:generatePiece()
|
||||||
return self.piece
|
return "I"
|
||||||
end
|
end
|
||||||
|
|
||||||
return AlwaysRandomizer
|
return AlwaysRandomizer
|
||||||
|
|||||||
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
|
||||||
@@ -3,10 +3,15 @@ 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()
|
||||||
|
if self.first then
|
||||||
|
self.first = false
|
||||||
|
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||||
|
else
|
||||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
local x = math.random(7)
|
local x = math.random(7)
|
||||||
@@ -15,6 +20,7 @@ function History4RollsRandomizer:generatePiece()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function History4RollsRandomizer:updateHistory(shape)
|
function History4RollsRandomizer:updateHistory(shape)
|
||||||
table.remove(self.history, 1)
|
table.remove(self.history, 1)
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ 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()
|
||||||
|
if self.first then
|
||||||
|
self.first = false
|
||||||
|
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||||
|
else
|
||||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||||
for i = 1, 6 do
|
for i = 1, 6 do
|
||||||
local x = math.random(7)
|
local x = math.random(7)
|
||||||
@@ -15,6 +20,7 @@ function History6RollsRandomizer:generatePiece()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:updateHistory(shape)
|
function History6RollsRandomizer:updateHistory(shape)
|
||||||
table.remove(self.history, 1)
|
table.remove(self.history, 1)
|
||||||
|
|||||||
@@ -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]
|
||||||
end
|
until not inHistory(x, prevent)
|
||||||
|
self.first = false
|
||||||
function History6RollsRandomizer:generatePiece()
|
else
|
||||||
for i = 1, 6 do
|
for i = 1, 6 do
|
||||||
local x = self:getBagPiece(math.random(31))
|
index = math.random(#self.pool)
|
||||||
|
x = self.pool[index]
|
||||||
if not inHistory(x, self.history) or i == 6 then
|
if not inHistory(x, self.history) or i == 6 then
|
||||||
return self:updateHistory(x)
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
self.pool[index] = self:updateHistory(x)
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
|
||||||
function History6RollsRandomizer:updateHistory(shape)
|
function History6Rolls35PoolRandomizer:updateHistory(shape)
|
||||||
self.bag_counts[shape] = self.bag_counts[shape] - 1
|
table.remove(self.history, 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()
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ ARS.spawn_positions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ARS.big_spawn_positions = {
|
ARS.big_spawn_positions = {
|
||||||
I = { x=2, y=2 },
|
I = { x=3, y=2 },
|
||||||
J = { x=2, y=3 },
|
J = { x=2, y=3 },
|
||||||
L = { x=2, y=3 },
|
L = { x=2, y=3 },
|
||||||
O = { x=2, y=3 },
|
O = { x=3, y=3 },
|
||||||
S = { x=2, y=3 },
|
S = { x=2, y=3 },
|
||||||
T = { x=2, y=3 },
|
T = { x=2, y=3 },
|
||||||
Z = { x=2, y=3 },
|
Z = { x=2, y=3 },
|
||||||
@@ -81,19 +81,27 @@ 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
|
||||||
|
self:onPieceRotate(piece, grid)
|
||||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||||
self:onPieceRotate(piece, grid)
|
|
||||||
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})
|
|
||||||
self:onPieceRotate(piece, grid)
|
self:onPieceRotate(piece, grid)
|
||||||
|
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -102,7 +110,14 @@ function ARS:onPieceDrop(piece, grid)
|
|||||||
piece.lock_delay = 0 -- step reset
|
piece.lock_delay = 0 -- step reset
|
||||||
end
|
end
|
||||||
|
|
||||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
function ARS:get180RotationValue()
|
||||||
|
if config.gamesettings.world_reverse == 3 then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||||
|
|
||||||
return ARS
|
return ARS
|
||||||
|
|||||||