Compare commits

..

103 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
5ce0686e1a Update version to beta5 2021-03-26 23:11:59 -04:00
Ishaan Bhardwaj
3cf496ba98 Fixed a Ti-ARS floorkick issue 2021-03-16 14:13:44 -04:00
Ishaan Bhardwaj
5ddc6ec561 Fixed a priority order bug with Ti-ARS and ACE-ARS 2021-03-11 20:16:23 -05:00
Ishaan Bhardwaj
b91ffc913b CRS no longer locks in midair 2021-03-11 15:29:24 -05:00
Ishaan Bhardwaj
ab445ff699 Cleaned up love.load 2021-03-11 09:24:19 -05:00
Ishaan Bhardwaj
7b7a255bf8 Fullscreen swap now persists between reboots 2021-03-11 08:33:05 -05:00
Ishaan Bhardwaj
57721ed35d Shrunk the bone coloring code 2021-03-10 16:30:45 -05:00
Ishaan Bhardwaj
8383d3f445 Debug print statement removal 2021-03-10 14:06:57 -05:00
Ishaan Bhardwaj
116284f31c Fixed colour scheme issues for non-standard piece sets 2021-03-10 13:58:08 -05:00
Ishaan Bhardwaj
2189e3a7b8 Made a previous fix to soft drop points obsolete w/ new fix 2021-03-10 13:30:29 -05:00
Ishaan Bhardwaj
b1d325b714 Fixed negative soft drop and hard drop points again
For finer control of piece drops, use GameMode:onPieceDrop
2021-03-09 16:35:57 -05:00
Ishaan Bhardwaj
4ab5e3747a Fixed an issue with the generic bag randomizer 2021-03-09 13:00:43 -05:00
Ishaan Bhardwaj
9761ead48f Fixed an odd bug where the score wouldn't reset in Big A2 2021-03-08 21:12:05 -05:00
Ishaan Bhardwaj
8dedc8a70e Cut down Big A2's size 2021-03-08 20:59:51 -05:00
Ishaan Bhardwaj
21769f21c8 Sakura removed from the main game, will be re-added after mass bugfix 2021-03-08 18:53:01 -05:00
Ishaan Bhardwaj
5cf26b4500 Merge branch 'master' of https://github.com/sashlilac/cambridge 2021-03-08 12:41:54 -05:00
Ishaan Bhardwaj
4b4a968632 Fixed a bone block drawing issue 2021-03-08 12:41:46 -05:00
Ishaan Bhardwaj
e0d98de50d The Discord server has been reopened! 2021-03-08 11:11:26 -05:00
Ishaan Bhardwaj
4992ea733c Made the bone block world sprite a bit brighter 2021-03-08 10:20:25 -05:00
Ishaan Bhardwaj
30ca434027 Fixed ACE-ARS to floorkick infinitely 2021-03-07 22:24:11 -05:00
Ishaan Bhardwaj
502a50d004 Merge pull request #16 from SashLilac/hat_handling
Fixed the handling of joystick hats.
2021-03-07 21:17:59 -05:00
Joe Z
36f5287a39 Fixed the hat input mapping. 2021-03-07 20:43:55 -05:00
Ishaan Bhardwaj
a9bbe4a08d Init hat handling 2021-03-07 16:42:33 -05:00
Ishaan Bhardwaj
ee431f5fd8 Revert "(Hopefully) Fixed an obscure bug with SOCD and joystick hats"
This did not fix it.
This reverts commit 36f2672e06.
2021-03-07 16:29:01 -05:00
Ishaan Bhardwaj
36f2672e06 (Hopefully) Fixed an obscure bug with SOCD and joystick hats 2021-03-07 16:20:43 -05:00
Ishaan Bhardwaj
6ecea7edb1 Fixed an issue where axes would not detect left or up 2021-03-07 16:03:02 -05:00
Ishaan Bhardwaj
dc764b9177 Fixed the timer in Sakura to be the correct value 2021-03-07 15:52:55 -05:00
Ishaan Bhardwaj
5a1494cb5a Fixed a timer display issue in Sakura 2021-03-07 15:52:24 -05:00
Ishaan Bhardwaj
684c4f5b78 Sakura stage time limit fix 2021-03-07 15:18:13 -05:00
Ishaan Bhardwaj
b568c0fe69 Removed the credit roll from AX 2021-03-07 09:49:07 -05:00
Ishaan Bhardwaj
2ea75cdfaf Fixed a corner case in the last commit 2021-03-06 22:13:38 -05:00
Ishaan Bhardwaj
1f0b43f1b7 ACTUALLY fixed negative drop points 2021-03-06 22:00:30 -05:00
Ishaan Bhardwaj
40bdc5ed99 Revert "Fixed negative drop points"
This commit didn't actually fix the issue.
This reverts commit 33f2a96ae8.
2021-03-06 21:54:36 -05:00
Ishaan Bhardwaj
33f2a96ae8 Fixed negative drop points 2021-03-06 21:39:13 -05:00
Ishaan Bhardwaj
846013ce7a Indentation fix in funcs.lua 2021-03-04 19:05:43 -05:00
Ishaan Bhardwaj
37c85adc75 Fixed clamping once more 2021-03-04 19:05:20 -05:00
Ishaan Bhardwaj
e7bb44deb4 Fixed clamping 2021-03-04 18:44:30 -05:00
Ishaan Bhardwaj
57518dc299 Removed some future features that I committed by accident 2021-03-04 15:18:32 -05:00
Ishaan Bhardwaj
0453a3db97 Fixed an issue where first piece IHS was possible when it shouldn't have been 2021-03-04 15:16:23 -05:00
Ishaan Bhardwaj
b85de17e51 Last LCD added to fix up line clear animations 2021-03-03 11:54:43 -05:00
Ishaan Bhardwaj
163b8f6cc5 Added a version display 2021-03-03 10:33:10 -05:00
Ishaan Bhardwaj
7250bee619 Ti randomizer no longer draws Z as first piece 2021-03-02 20:31:56 -05:00
Ishaan Bhardwaj
83de216408 Touched up screenshotting a bit 2021-03-01 20:37:44 -05:00
Ishaan Bhardwaj
ba235c8a41 Credits update <3 2021-02-28 18:40:53 -05:00
Ishaan Bhardwaj
ca18d090c9 Split keyboard and joystick input config screens 2021-02-25 14:41:13 -05:00
Ishaan Bhardwaj
a3a27d2566 Refactored joystick input handling 2021-02-24 16:58:42 -05:00
Ishaan Bhardwaj
b15cd9802f Updated DAS last key setting to not use hacky workaround
DAS last key is off by default
2021-02-22 21:43:01 -05:00
Ishaan Bhardwaj
4c4a818c5c Race 40 added to main game, PAIRS moved to modpack 2021-02-21 23:19:53 -05:00
Ishaan Bhardwaj
716de2814b More bigint type checks added
Strict checking is still off, however a check can be coerced
2021-02-21 20:38:16 -05:00
Ishaan Bhardwaj
bf19f49323 Add piece last rotated events 2021-02-21 10:48:15 -05:00
Ishaan Bhardwaj
1234e78354 Refactored immobile detection 2021-02-21 10:41:05 -05:00
Ishaan Bhardwaj
9129503d54 Fixed a sound effect handle with negative gravity 2021-02-21 10:08:58 -05:00
Ishaan Bhardwaj
eae58f11e9 Fixed a clipping issue with negative gravity
Gamemodes are able to define their own piece class behavior to override negative gravity handling
2021-02-21 10:05:09 -05:00
Ishaan Bhardwaj
3cf5daeb2e Piece class now handles negative gravity correctly 2021-02-21 09:52:50 -05:00
Ishaan Bhardwaj
1dfe68ccff onExit call for exiting prematurely 2021-02-19 15:58:00 -05:00
Ishaan Bhardwaj
8a459b68ba Allowed gamemode and ruleset objects to control each other
Also added GameMode:onExit(), which triggers on game exit or retry
2021-02-19 11:01:18 -05:00
Ishaan Bhardwaj
cb2b693bcb Fixed T-floorkick behavior in Ti/ACE ARS 2021-02-18 21:04:03 -05:00
Ishaan Bhardwaj
ef6d156d38 Turned draw offsets and above field offsets into function calls 2021-02-18 15:09:27 -05:00
Ishaan Bhardwaj
83e498534c Merge branch 'master' of https://github.com/sashlilac/cambridge 2021-02-18 12:01:05 -05:00
Ishaan Bhardwaj
8f19c73e2a Simultaneous keyboard and joystick inputs implemented!
Implements #9!!!
2021-02-18 12:00:57 -05:00
Ishaan Bhardwaj
e36b855ff7 The Discord server is no longer sponsored by the project. 2021-02-18 10:42:19 -05:00
Ishaan Bhardwaj
23b58951cb World rule Survival A2 has a lenient torikan time 2021-02-17 22:46:33 -05:00
Ishaan Bhardwaj
1d73916b7c Arika-SRS rulesets no longer lock immediately 2021-02-17 18:29:14 -05:00
Ishaan Bhardwaj
3947e9f02f Fix the drop block lock rotation with SRS 2021-02-17 17:31:16 -05:00
Ishaan Bhardwaj
99b15803ee Adjusted 0 ARR to trigger onPieceMove multiple times 2021-02-17 17:21:51 -05:00
Ishaan Bhardwaj
d350b25726 Forgot to set guideline SRS to always rotate 2021-02-17 14:52:05 -05:00
Ishaan Bhardwaj
44e4d00172 Merge branch 'master' of https://github.com/sashlilac/cambridge 2021-02-17 14:48:42 -05:00
Ishaan Bhardwaj
31e2529265 Upward kicks for SRS count toward rotation limit 2021-02-17 14:48:35 -05:00
Ishaan Bhardwaj
ea7c75f0b3 Cambridge Discord Server temp. decommissioned
Please contact Milla#7746 on Discord for help.
2021-02-17 10:45:07 -05:00
Ishaan Bhardwaj
714c6b5e99 Floorkicks reworked (read comments)
If not classic lock, upward kicks reset to the top of the tile
2021-02-16 23:28:54 -05:00
Ishaan Bhardwaj
6a5d5a9c88 Fixed some modes' getNextPiece routines 2021-02-16 17:02:13 -05:00
Ishaan Bhardwaj
03491ba151 Strategy mode endgame nerfed 2021-02-16 16:57:31 -05:00
Ishaan Bhardwaj
6e22e3d15b Ti-ARS autolock fix 2021-02-16 16:19:51 -05:00
Ishaan Bhardwaj
66ab5992ad Added onPieceMove/Rotate/Drop for gamemodes 2021-02-16 15:27:57 -05:00
Ishaan Bhardwaj
2c07c2a58c BigInt changes, read extended description
Disabled strict type checking, can be re-enabled in bleeding edge. (This is done so bigint ops run faster)
Added a negation method and updated the corresponding metamethod to use it.
2021-02-16 13:03:53 -05:00
Ishaan Bhardwaj
a4d3f3bffc Update README.md 2021-02-16 13:00:07 -05:00
Ishaan Bhardwaj
9ac60cbb5e afterLineClear func added and splits time draw fix 2021-02-15 12:26:52 -05:00
Ishaan Bhardwaj
cdd846c3e6 Made the volume sliders scroll more consistently 2021-02-13 22:00:45 -05:00
Ishaan Bhardwaj
33d260b753 Removed the print statement from A2 2021-02-12 23:31:13 -05:00
Ishaan Bhardwaj
1644fcdf8e Bigint exponentiation by 1 now returns a clone 2021-02-12 10:05:04 -05:00
Ishaan Bhardwaj
f3c1cf6e1f Fixed an issue where DS-World wouldn't harddrop 2021-02-11 22:11:35 -05:00
Ishaan Bhardwaj
06a8a2ebf7 Mandate safelock on 0 ARE rulesets/modes 2021-02-11 22:08:52 -05:00
Ishaan Bhardwaj
15354ce004 dropToBottom no longer resets lock delay
it's already handled by the rulesets anyhow
2021-02-11 21:20:23 -05:00
Ishaan Bhardwaj
af02cd3467 Classic lock (GB/NES-like) added as a gamemode var 2021-02-11 15:46:56 -05:00
Ishaan Bhardwaj
acb05918c1 Custom line clear animations 2021-02-10 23:10:10 -05:00
Ishaan Bhardwaj
b644c8e457 Revert "Default line clear animation set to fadeout"
Please, reminder to self, TEST YOUR COMMITS.
This reverts commit 288961e12a.
2021-02-10 22:46:58 -05:00
Ishaan Bhardwaj
288961e12a Default line clear animation set to fadeout 2021-02-10 22:41:07 -05:00
Ishaan Bhardwaj
a047e51681 Framework for custom line clear animations added
Colored fadeout is the default
2021-02-10 18:35:51 -05:00
Ishaan Bhardwaj
77f24f5ee5 Human readable bigint output changes 2021-02-10 12:45:55 -05:00
Ishaan Bhardwaj
32c2274bef Optimized bigint exponentiation (again) 2021-02-10 11:38:10 -05:00
Ishaan Bhardwaj
4920e5de1c Added another type check to the bigint 2021-02-10 11:15:56 -05:00
Ishaan Bhardwaj
8418fc8ab7 Update README.md 2021-02-10 10:32:18 -05:00
Ishaan Bhardwaj
711a5120f1 Update README.md 2021-02-10 10:31:52 -05:00
Ishaan Bhardwaj
e7c3c9446a Cambridge banner looks better on dark theme now
Courtesy of @sinefuse
2021-02-10 09:05:10 -05:00
Ishaan Bhardwaj
3ac39acd7a Removed bigint comparison metamethods (read below)
Use bigint.compare from now on
2021-02-09 12:27:57 -05:00
Ishaan Bhardwaj
d0505251b3 Spawn positions now ruleset dependent
Is configurable in options
2021-02-08 23:23:50 -05:00
Ishaan Bhardwaj
bb0fe2ac20 BigInt now has a digits method (read comments)
Kind of unnecessary but included for completeness
2021-02-08 16:56:06 -05:00
Ishaan Bhardwaj
986ebac47f BigInt division fixed 2021-02-08 16:07:48 -05:00
Ishaan Bhardwaj
9799147f96 Revert "BigInt fixes and optimization (read comments)"
Apparently division *still* isn't being handled correctly.
This reverts commit 1dda12e4be.
2021-02-08 14:53:19 -05:00
Ishaan Bhardwaj
1dda12e4be BigInt fixes and optimization (read comments)
Fixed a nasty division bug where intermediate operations could result in negative zero. Optimized exponentiation to use exponentiation by squaring.
2021-02-08 14:10:34 -05:00
Ishaan Bhardwaj
38947e00c0 Added a tostring function for bigints 2021-02-08 10:34:47 -05:00
Ishaan Bhardwaj
035f6dd7b4 Fixed big division when (big1 < big2) 2021-02-08 10:23:10 -05:00
Ishaan Bhardwaj
aa3eadc93d Update README.md 2021-02-08 09:00:51 -05:00
39 changed files with 1156 additions and 1722 deletions

View File

@@ -1,13 +1,15 @@
![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png) ![Cambridge Banner](https://t-sp.in/public/img/cambridge.png)
Cambridge Cambridge
========= =========
Welcome to Cambridge, the next open-source falling-block game engine! Welcome to Cambridge, the next open-source falling-block game engine!
This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)! The project is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)!
Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4 The Discord server has been reopened! https://discord.gg/AADZUmgsph
The game also has a website now with more detail than seen on this README: https://t-sp.in/cambridge
Credits Credits
------- -------
@@ -17,19 +19,7 @@ Credits
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting! - [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 Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
The following people in no particular order also helped with the project: More special thanks can be found in-game, under the "Credits" menu.
- [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)
![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png) ![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png)
@@ -40,7 +30,13 @@ Playing the game
You do not need LÖVE on Windows, as it comes bundled with the program. You do not need LÖVE on Windows, as it comes bundled with the program.
To get the stable release, simply download the ZIP in the latest release. All assets needed are bundled with the executable. #### Stable release
To get the stable release, simply download either `cambridge-win32.zip` (32-bit) or `cambridge-windows.zip` (64-bit) in the [latest release](https://github.com/sashlilac/cambridge/releases/latest).
All assets needed are bundled with the executable.
#### Bleeding edge
If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip). If you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
@@ -82,13 +78,7 @@ It should run automatically!
## Installing modpacks ## Installing modpacks
Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically. For instructions on how to install modpacks, go to [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
You can also load custom assets through this way, assuming you preserve the directory structure.
**WARNING:** The .exe / .love files and the bleeding edge releases have different save directories. Read the above link carefully!
For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
License License
------- -------

View File

@@ -87,6 +87,9 @@ function table.contains(table, element)
return false return false
end end
function clamp(a, b, c) function clamp(x, min, max)
return math.min(a, math.max(b, c)) if max < min then
end min, max = max, min
end
return x < min and min or (x > max and max or x)
end

View File

@@ -2,12 +2,39 @@
-- If this variable is true, then strict type checking is performed for all -- If this variable is true, then strict type checking is performed for all
-- operations. This may result in slower code, but it will allow you to catch -- operations. This may result in slower code, but it will allow you to catch
-- errors and bugs earlier. -- errors and bugs earlier.
local strict = true local strict = false
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
local bigint = {} local bigint = {}
local mt = {
__add = function(lhs, rhs)
return bigint.add(lhs, rhs)
end,
__unm = function()
return bigint.negate(self)
end,
__sub = function(lhs, rhs)
return bigint.subtract(lhs, rhs)
end,
__mul = function(lhs, rhs)
return bigint.multiply(lhs, rhs)
end,
__div = function(lhs, rhs)
return bigint.divide(lhs, rhs)
end,
__mod = function(lhs, rhs)
return bigint.modulus(lhs, rhs)
end,
__pow = function(lhs, rhs)
return bigint.exponentiate(lhs, rhs)
end,
__tostring = function()
return bigint.unserialize(self, "s")
end
}
local named_powers = require("libs.bigint.named-powers-of-ten") local named_powers = require("libs.bigint.named-powers-of-ten")
-- Create a new bigint or convert a number or string into a big -- Create a new bigint or convert a number or string into a big
@@ -28,43 +55,7 @@ function bigint.new(num)
return newint return newint
end end
setmetatable(self, { setmetatable(self, mt)
__add = function(lhs, rhs)
return bigint.add(lhs, rhs)
end,
__unm = function()
if (self.sign == "+") then
self.sign = "-"
else
self.sign = "+"
end
return self
end,
__sub = function(lhs, rhs)
return bigint.subtract(lhs, rhs)
end,
__mul = function(lhs, rhs)
return bigint.multiply(lhs, rhs)
end,
__div = function(lhs, rhs)
return bigint.divide(lhs, rhs)
end,
__mod = function(lhs, rhs)
return bigint.modulus(lhs, rhs)
end,
__pow = function(lhs, rhs)
return bigint.exponentiate(lhs, rhs)
end,
__eq = function(lhs, rhs)
return bigint.compare(lhs, rhs, "==")
end,
__lt = function(lhs, rhs)
return bigint.compare(lhs, rhs, "<")
end,
__le = function(lhs, rhs)
return bigint.compare(lhs, rhs, "<=")
end
})
if (num) then if (num) then
local num_string = tostring(num) local num_string = tostring(num)
@@ -84,11 +75,13 @@ end
-- forced by supplying "true" as the second argument. -- forced by supplying "true" as the second argument.
function bigint.check(big, force) function bigint.check(big, force)
if (strict or force) then if (strict or force) then
assert(getmetatable(big) == mt, "at least one arg is not a bigint")
assert(#big.digits > 0, "bigint is empty") assert(#big.digits > 0, "bigint is empty")
assert(type(big.sign) == "string", "bigint is unsigned") assert(big.sign == "+" or big.sign == "-", "bigint is unsigned")
for _, digit in pairs(big.digits) do for _, digit in pairs(big.digits) do
assert(type(digit) == "number", digit .. " is not a number") assert(type(digit) == "number", "at least one digit is invalid")
assert(digit < 10, digit .. " is greater than or equal to 10") assert(digit <= 9 and digit >= 0, digit .. " is not between 0 and 9")
assert(math.floor(digit) == digit, digit .. " is not an integer")
end end
end end
return true return true
@@ -103,6 +96,24 @@ function bigint.abs(big)
return result return result
end end
-- Return a new big with the same digits but the opposite sign (negation)
function bigint.negate(big)
bigint.check(big)
local result = big:clone()
if (result.sign == "+") then
result.sign = "-"
else
result.sign = "+"
end
return result
end
-- Return the number of digits in the big
function bigint.digits(big)
bigint.check(big)
return #big.digits
end
-- Convert a big to a number or string -- Convert a big to a number or string
function bigint.unserialize(big, output_type, precision) function bigint.unserialize(big, output_type, precision)
bigint.check(big) bigint.check(big)
@@ -137,7 +148,7 @@ function bigint.unserialize(big, output_type, precision)
-- Unserialization to human-readable form or scientific notation only -- Unserialization to human-readable form or scientific notation only
-- requires reading the first few digits -- requires reading the first few digits
if (precision == nil) then if (precision == nil) then
precision = 3 precision = math.min(#big.digits, 3)
else else
assert(precision > 0, "Precision cannot be less than 1") assert(precision > 0, "Precision cannot be less than 1")
assert(math.floor(precision) == precision, assert(math.floor(precision) == precision,
@@ -146,17 +157,18 @@ function bigint.unserialize(big, output_type, precision)
-- num is the first (precision + 1) digits, the first being separated by -- num is the first (precision + 1) digits, the first being separated by
-- a decimal point from the others -- a decimal point from the others
num = num .. big.digits[1] num = num .. math.floor(big.digits[1])
if (precision > 1) then if (precision > 1) then
num = num .. "." num = num .. "."
for i = 1, (precision - 1) do for i = 1, (precision - 1) do
num = num .. big.digits[i + 1] num = num .. math.floor(big.digits[i + 1])
end end
end end
if ((output_type == "human-readable") if ((output_type == "human-readable")
or (output_type == "human") or (output_type == "human")
or (output_type == "h")) then or (output_type == "h"))
and (#big.digits >= 3 and #big.digits <= 10002) then
-- Human-readable output contributed by 123eee555 -- Human-readable output contributed by 123eee555
local name local name
@@ -435,7 +447,6 @@ function bigint.multiply(big1, big2)
return result return result
end end
-- Raise a big to a positive integer or big power (TODO: negative integer power) -- Raise a big to a positive integer or big power (TODO: negative integer power)
function bigint.exponentiate(big, power) function bigint.exponentiate(big, power)
-- Type checking for big done by bigint.multiply -- Type checking for big done by bigint.multiply
@@ -446,13 +457,23 @@ function bigint.exponentiate(big, power)
if (bigint.compare(exp, bigint.new(0), "==")) then if (bigint.compare(exp, bigint.new(0), "==")) then
return bigint.new(1) return bigint.new(1)
elseif (bigint.compare(exp, bigint.new(1), "==")) then elseif (bigint.compare(exp, bigint.new(1), "==")) then
return big return big:clone()
else else
local result = big:clone() local result = bigint.new(1)
local base = big:clone()
while (bigint.compare(exp, bigint.new(1), ">")) do while (true) do
result = bigint.multiply(result, big) if (bigint.compare(
exp = bigint.subtract(exp, bigint.new(1)) bigint.modulus(exp, bigint.new(2)), bigint.new(1), "=="
)) then
result = bigint.multiply(result, base)
end
if (bigint.compare(exp, bigint.new(1), "==")) then
break
else
exp = bigint.divide(exp, bigint.new(2))
base = bigint.multiply(base, base)
end
end end
return result return result
@@ -468,7 +489,7 @@ function bigint.divide_raw(big1, big2)
if (bigint.compare(big1, big2, "==")) then if (bigint.compare(big1, big2, "==")) then
return bigint.new(1), bigint.new(0) return bigint.new(1), bigint.new(0)
elseif (bigint.compare(big1, big2, "<")) then elseif (bigint.compare(big1, big2, "<")) then
return bigint.new(0), bigint.new(0) return bigint.new(0), big1:clone()
else else
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero") assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
assert(big1.sign == "+", "error: big1 is not positive") assert(big1.sign == "+", "error: big1 is not positive")
@@ -476,54 +497,35 @@ function bigint.divide_raw(big1, big2)
local result = bigint.new() local result = bigint.new()
local dividend = bigint.new() -- Dividend of a single operation, not the local dividend = bigint.new() -- Dividend of a single operation
-- dividend of the overall function
local divisor = big2:clone()
local factor = 1
-- Walk left to right among digits in the dividend, like in long local neg_zero = bigint.new(0)
-- division neg_zero.sign = "-"
for _, digit in pairs(big1.digits) do
dividend.digits[#dividend.digits + 1] = digit
-- The dividend is smaller than the divisor, so a zero is appended for i = 1, #big1.digits do
-- to the result and the loop ends -- Fixes a negative zero bug
if (bigint.compare(dividend, divisor, "<")) then if (#dividend.digits ~= 0) and (bigint.compare(dividend, neg_zero, "==")) then
if (#result.digits > 0) then -- Don't add leading zeroes dividend = bigint.new()
result.digits[#result.digits + 1] = 0 end
end
else table.insert(dividend.digits, big1.digits[i])
-- Find the maximum number of divisors that fit into the
-- dividend local factor = bigint.new(0)
factor = 0 while bigint.compare(dividend, big2, ">=") do
while (bigint.compare(divisor, dividend, "<=")) do dividend = bigint.subtract(dividend, big2)
divisor = bigint.add(divisor, big2) factor = bigint.add(factor, bigint.new(1))
factor = factor + 1
end
-- Append the factor to the result
if (factor == 10) then
-- Fixes a weird bug that introduces a new bug if fixed by
-- changing the comparison in the while loop to "<="
result.digits[#result.digits] = 1
result.digits[#result.digits + 1] = 0
else
result.digits[#result.digits + 1] = factor
end
-- Subtract the divisor from the dividend to obtain the
-- remainder, which is the new dividend for the next loop
dividend = bigint.subtract(dividend,
bigint.subtract(divisor, big2))
-- Reset the divisor
divisor = big2:clone()
end end
for i = 0, #factor.digits - 1 do
result.digits[#result.digits + 1 - i] = factor.digits[i + 1]
end
end
-- Remove leading zeros from result
while (result.digits[1] == 0) do
table.remove(result.digits, 1)
end end
-- The remainder of the final loop is returned as the function's
-- overall remainder
return result, dividend return result, dividend
end end
end end

View File

@@ -13,7 +13,31 @@ function loadFromFile(filename)
return save_data[1] return save_data[1]
end end
function initConfig()
if not config.das then config.das = 10 end
if not config.arr then config.arr = 2 end
if not config.dcd then config.dcd = 0 end
if not config.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end
if config.fullscreen == nil then config.fullscreen = false end
if config.secret == nil then config.secret = false end
if not config.gamesettings then config.gamesettings = {} end
for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1
end
end
if not config.input then
scene = KeyConfigScene()
else
if config.current_mode then current_mode = config.current_mode end
if config.current_ruleset then current_ruleset = config.current_ruleset end
scene = TitleScene()
end
end
function saveConfig() function saveConfig()
binser.writeFile('config.sav', config) binser.writeFile('config.sav', config)

1
load/version.lua Normal file
View File

@@ -0,0 +1 @@
version = "v0.3-beta5"

View File

@@ -8,11 +8,14 @@ function love.load()
require "load.bgm" require "load.bgm"
require "load.save" require "load.save"
require "load.bigint" require "load.bigint"
require "load.version"
loadSave() loadSave()
require "scene" require "scene"
--config["side_next"] = false --config["side_next"] = false
--config["reverse_rotate"] = true --config["reverse_rotate"] = true
config["fullscreen"] = false --config["das_last_key"] = false
--config["fullscreen"] = false
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
@@ -20,35 +23,16 @@ function love.load()
GLOBAL_CANVAS = love.graphics.newCanvas() GLOBAL_CANVAS = love.graphics.newCanvas()
-- init config -- init config
if not config.das then config.das = 10 end initConfig()
if not config.arr then config.arr = 2 end
if not config.dcd then config.dcd = 0 end
if not config.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end
if config.secret == nil then config.secret = false
elseif config.secret == true then playSE("welcome") end
if not config.gamesettings then love.window.setFullscreen(config["fullscreen"])
config.gamesettings = {} if config.secret then playSE("welcome") end
config["das_last_key"] = false
else
config["das_last_key"] = config.gamesettings.das_last_key == 2
end
for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1
end
end
if not config.input then
scene = InputConfigScene()
else
if config.current_mode then current_mode = config.current_mode end
if config.current_ruleset then current_ruleset = config.current_ruleset end
scene = TitleScene()
end
-- import custom modules
initModules()
end
function initModules()
game_modes = {} game_modes = {}
mode_list = love.filesystem.getDirectoryItems("tetris/modes") mode_list = love.filesystem.getDirectoryItems("tetris/modes")
for i=1,#mode_list do for i=1,#mode_list do
@@ -69,7 +53,6 @@ function love.load()
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end) return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
table.sort(rulesets, function(a,b) table.sort(rulesets, function(a,b)
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end) return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
end end
local TARGET_FPS = 60 local TARGET_FPS = 60
@@ -139,6 +122,7 @@ 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"]
saveConfig()
love.window.setFullscreen(config["fullscreen"]) love.window.setFullscreen(config["fullscreen"])
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
scene = InputConfigScene() scene = InputConfigScene()
@@ -153,9 +137,11 @@ function love.keypressed(key, scancode)
-- f12 is reserved for saving screenshots -- f12 is reserved for saving screenshots
elseif scancode == "f12" then elseif scancode == "f12" then
local ss_name = os.date("ss/%Y-%m-%d_%H-%M-%S.png") local ss_name = os.date("ss/%Y-%m-%d_%H-%M-%S.png")
if not love.filesystem.getInfo("ss") then local info = love.filesystem.getInfo("ss")
love.filesystem.createDirectory("ss") if not info or info.type ~= "directory" then
end love.filesystem.remove("ss")
love.filesystem.createDirectory("ss")
end
print("Saving screenshot as "..ss_name) print("Saving screenshot as "..ss_name)
GLOBAL_CANVAS:newImageData():encode("png", ss_name) GLOBAL_CANVAS:newImageData():encode("png", ss_name)
-- function keys are reserved -- function keys are reserved
@@ -228,13 +214,13 @@ function love.joystickaxis(joystick, axis, value)
config.input.joysticks[joystick:getName()].axes and config.input.joysticks[joystick:getName()].axes and
config.input.joysticks[joystick:getName()].axes[axis] config.input.joysticks[joystick:getName()].axes[axis]
then then
if math.abs(value) >= 0.5 then if math.abs(value) >= 1 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"] input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
end end
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
end end
if math.abs(value) >= 0.5 then if math.abs(value) >= 1 then
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value}) scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
else else
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value}) scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
@@ -242,6 +228,14 @@ function love.joystickaxis(joystick, axis, value)
end end
end end
local last_hat_direction = ""
local directions = {
["u"] = "up",
["d"] = "down",
["l"] = "left",
["r"] = "right",
}
function love.joystickhat(joystick, hat, direction) function love.joystickhat(joystick, hat, direction)
local input_pressed = nil local input_pressed = nil
local has_hat = false local has_hat = false
@@ -258,17 +252,47 @@ function love.joystickhat(joystick, hat, direction)
has_hat = true has_hat = true
end end
if input_pressed then if input_pressed then
scene:onInputPress({input=input_pressed, type="joyhat", name=joystick:getName(), hat=hat, direction=direction}) for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
scene:onInputPress({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
elseif has_hat then elseif has_hat then
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do 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}) scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end end
last_hat_direction = ""
elseif direction ~= "c" then elseif direction ~= "c" then
scene:onInputPress({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction}) for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
scene:onInputPress({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
scene:onInputRelease({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
else else
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do 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}) scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end end
last_hat_direction = ""
end end
end end

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 153 B

View File

@@ -11,6 +11,8 @@ function Scene:onInputRelease() end
ExitScene = require "scene.exit" ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"
ModeSelectScene = require "scene.mode_select" ModeSelectScene = require "scene.mode_select"
KeyConfigScene = require "scene.key_config"
StickConfigScene = require "scene.stick_config"
InputConfigScene = require "scene.input_config" InputConfigScene = require "scene.input_config"
GameConfigScene = require "scene.game_config" GameConfigScene = require "scene.game_config"
TuningScene = require "scene.tuning" TuningScene = require "scene.tuning"

View File

@@ -30,7 +30,7 @@ function CreditsScene:render()
love.graphics.setFont(font_3x5_4) love.graphics.setFont(font_3x5_4)
love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2) love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2)
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1500 - self.frames / 2, 240)) love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1750 - self.frames / 2, 240))
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.print("Game Developers", 320, 550 - self.frames / 2) love.graphics.print("Game Developers", 320, 550 - self.frames / 2)
@@ -47,8 +47,12 @@ function CreditsScene:render()
"RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" .. "RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" ..
"Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" .. "Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" ..
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\nMarkGamed7794\n" .. "CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\nMarkGamed7794\n" ..
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nThe Absolute PLUS Discord\nTetra Legends Discord\n" .. "pokemonfan1937\nSimon\nstratus\nZaptorZap\nArchina\nOliver\ncolour_thief\n" ..
"Tetra Online Discord\nMultimino Discord\nCambridge Discord\nAnd to you, the player!", "Caithness\nkdex\nzid\nsaphie\nSuper302\nAurora\nswitchpalacecorner\nKitaru\n" ..
"JBroms\nMany more I definitely missed!\n" ..
"The Absolute PLUS Discord\nTetra Legends Discord\nTetra Online Discord\n" ..
"Multimino Discord\nHard Drop Discord\nCambridge Discord (R.I.P.)\n" ..
"And to you, the player!",
320, 940 - self.frames / 2 320, 940 - self.frames / 2
) )
end end

View File

@@ -7,10 +7,10 @@ require 'load.save'
function GameScene:new(game_mode, ruleset, inputs) function GameScene:new(game_mode, ruleset, inputs)
self.retry_mode = game_mode self.retry_mode = game_mode
self.retry_ruleset = ruleset self.retry_ruleset = ruleset
self.secret_inputs = copy(inputs) self.secret_inputs = inputs
self.game = game_mode(self.secret_inputs) self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset() self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset, self.secret_inputs) self.game:initialize(self.ruleset)
self.inputs = { self.inputs = {
left=false, left=false,
right=false, right=false,
@@ -83,6 +83,7 @@ function GameScene:render()
end end
self.game:drawGrid() self.game:drawGrid()
if self.game.lcd > 0 then self.game:drawLineClearAnimation() end
self.game:drawPiece() self.game:drawPiece()
self.game:drawNextQueue(self.ruleset) self.game:drawNextQueue(self.ruleset)
self.game:drawScoringInfo() self.game:drawScoringInfo()
@@ -117,15 +118,18 @@ function GameScene:onInputPress(e)
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)
self.game:onExit()
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene() scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
elseif e.input == "retry" then elseif e.input == "retry" then
switchBGM(nil) switchBGM(nil)
self.game:onExit()
scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs)
elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
self.paused = not self.paused self.paused = not self.paused
if self.paused then pauseBGM() if self.paused then pauseBGM()
else resumeBGM() end else resumeBGM() end
elseif e.input == "menu_back" then elseif e.input == "menu_back" then
self.game:onExit()
scene = ModeSelectScene() scene = ModeSelectScene()
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = true self.inputs[e.input] = true

View File

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

View File

@@ -2,162 +2,63 @@ local ConfigScene = Scene:extend()
ConfigScene.title = "Input Config" ConfigScene.title = "Input Config"
require 'load.save' local menu_screens = {
KeyConfigScene,
local configurable_inputs = { StickConfigScene
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
} }
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function ConfigScene:new() function ConfigScene:new()
self.input_state = 1 self.menu_state = 1
self.set_inputs = newSetInputs() DiscordRPC:update({
self.new_input = {} details = "In menus",
self.axis_timer = 0 state = "Changing input config",
})
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.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw( love.graphics.draw(
backgrounds["input_config"], backgrounds["input_config"],
0, 0, 0, 0, 0, 0,
0.5, 0.5 0.5, 0.5
) )
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_4)
for i, input in ipairs(configurable_inputs) do love.graphics.print("INPUT CONFIG", 80, 40)
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if self.set_inputs[input] then
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
end
end
if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press key or joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
end
self.axis_timer = self.axis_timer + 1 love.graphics.setFont(font_3x5_2)
love.graphics.print("Which controls do you want to change?", 80, 90)
love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 75, 118 + 50 * self.menu_state, 200, 33)
love.graphics.setFont(font_3x5_3)
love.graphics.setColor(1, 1, 1, 1)
for i, screen in pairs(menu_screens) do
love.graphics.printf(screen.title, 80, 120 + 50 * i, 200, "left")
end
end end
local function addJoystick(input, name) function ConfigScene:changeOption(rel)
if not input.joysticks then local len = table.getn(menu_screens)
input.joysticks = {} self.menu_state = (self.menu_state + len + rel - 1) % len + 1
end
if not input.joysticks[name] then
input.joysticks[name] = {}
end
end end
function ConfigScene:onInputPress(e) function ConfigScene:onInputPress(e)
if e.type == "key" then if e.input == "menu_decide" or e.scancode == "return" then
-- function keys, escape, and tab are reserved and can't be remapped playSE("main_decide")
if e.scancode == "escape" and config.input then scene = menu_screens[self.menu_state]()
-- cancel only if there was an input config already elseif e.input == "up" or e.scancode == "up" then
scene = SettingsScene() self:changeOption(-1)
elseif self.input_state > table.getn(configurable_inputs) then playSE("cursor")
if e.scancode == "return" then elseif e.input == "down" or e.scancode == "down" then
-- save new input, then load next scene self:changeOption(1)
config.input = self.new_input playSE("cursor")
saveConfig() elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
scene = TitleScene() scene = SettingsScene()
elseif e.scancode == "delete" or e.scancode == "backspace" then
-- retry
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
end
elseif e.scancode == "tab" then
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
self.input_state = self.input_state + 1
elseif e.scancode ~= "escape" then
-- all other keys can be configured
if not self.new_input.keys then
self.new_input.keys = {}
end
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
self.new_input.keys[e.scancode] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
elseif string.sub(e.type, 1, 3) == "joy" then
if self.input_state <= table.getn(configurable_inputs) then
if e.type == "joybutton" then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].buttons then
self.new_input.joysticks[e.name].buttons = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jbtn " ..
e.button ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].buttons[e.button] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
elseif e.type == "joyaxis" then
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].axes then
self.new_input.joysticks[e.name].axes = {}
end
if not self.new_input.joysticks[e.name].axes[e.axis] then
self.new_input.joysticks[e.name].axes[e.axis] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jaxis " ..
(e.value >= 1 and "+" or "-") .. e.axis ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.last_axis = e.axis
self.axis_timer = 0
end
elseif e.type == "joyhat" then
if e.direction ~= "c" then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].hats then
self.new_input.joysticks[e.name].hats = {}
end
if not self.new_input.joysticks[e.name].hats[e.hat] then
self.new_input.joysticks[e.name].hats[e.hat] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jhat " ..
e.hat .. " " .. e.direction ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
end end
end end
return ConfigScene return ConfigScene

101
scene/key_config.lua Normal file
View File

@@ -0,0 +1,101 @@
local KeyConfigScene = Scene:extend()
KeyConfigScene.title = "Key Config"
require 'load.save'
local configurable_inputs = {
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
}
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function KeyConfigScene:new()
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
DiscordRPC:update({
details = "In menus",
state = "Changing key config",
})
end
function KeyConfigScene:update()
end
function KeyConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["input_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setFont(font_3x5_2)
for i, input in ipairs(configurable_inputs) do
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if self.set_inputs[input] then
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
end
end
if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press key input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (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
end
function KeyConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" and config.input then
-- cancel only if there was an input config already
scene = InputConfigScene()
elseif self.input_state > table.getn(configurable_inputs) then
if e.scancode == "return" then
-- save new input, then load next scene
local had_config = config.input ~= nil
if not config.input then config.input = {} end
config.input.keys = self.new_input
saveConfig()
scene = had_config and InputConfigScene() or TitleScene()
elseif e.scancode == "delete" or e.scancode == "backspace" then
-- retry
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
end
elseif e.scancode == "tab" then
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
self.input_state = self.input_state + 1
elseif e.scancode ~= "escape" then
-- all other keys can be configured
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
self.new_input[e.scancode] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
return KeyConfigScene

150
scene/stick_config.lua Normal file
View File

@@ -0,0 +1,150 @@
local StickConfigScene = Scene:extend()
StickConfigScene.title = "Joystick Config"
require 'load.save'
local configurable_inputs = {
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
}
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function StickConfigScene:new()
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
self.axis_timer = 0
DiscordRPC:update({
details = "In menus",
state = "Changing joystick config",
})
end
function StickConfigScene:update()
end
function StickConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["input_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setFont(font_3x5_2)
for i, input in ipairs(configurable_inputs) do
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if self.set_inputs[input] then
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
end
end
if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
end
self.axis_timer = self.axis_timer + 1
end
local function addJoystick(input, name)
if not input[name] then
input[name] = {}
end
end
function StickConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" then
scene = InputConfigScene()
elseif self.input_state > table.getn(configurable_inputs) then
if e.scancode == "return" then
-- save new input, then load next scene
config.input.joysticks = self.new_input
saveConfig()
scene = InputConfigScene()
elseif e.scancode == "delete" or e.scancode == "backspace" then
-- retry
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
end
elseif e.scancode == "tab" then
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
self.input_state = self.input_state + 1
end
elseif string.sub(e.type, 1, 3) == "joy" then
if self.input_state <= table.getn(configurable_inputs) then
if e.type == "joybutton" then
addJoystick(self.new_input, e.name)
if not self.new_input[e.name].buttons then
self.new_input[e.name].buttons = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jbtn " ..
e.button ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input[e.name].buttons[e.button] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
elseif e.type == "joyaxis" then
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
addJoystick(self.new_input, e.name)
if not self.new_input[e.name].axes then
self.new_input[e.name].axes = {}
end
if not self.new_input[e.name].axes[e.axis] then
self.new_input[e.name].axes[e.axis] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jaxis " ..
(e.value >= 1 and "+" or "-") .. e.axis ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.last_axis = e.axis
self.axis_timer = 0
end
elseif e.type == "joyhat" then
if e.direction ~= "c" then
addJoystick(self.new_input, e.name)
if not self.new_input[e.name].hats then
self.new_input[e.name].hats = {}
end
if not self.new_input[e.name].hats[e.hat] then
self.new_input[e.name].hats[e.hat] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jhat " ..
e.hat .. " " .. e.direction ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
end
end
return StickConfigScene

View File

@@ -83,6 +83,7 @@ function TitleScene:render()
love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left") love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left")
end end
love.graphics.printf(version, 0, 460, love.graphics.getWidth() - 5, "right")
end end
function TitleScene:changeOption(rel) function TitleScene:changeOption(rel)

View File

@@ -111,18 +111,24 @@ function Grid:getClearedRowCount()
end end
function Grid:markClearedRows() function Grid:markClearedRows()
local block_table = {}
for row = 1, self.height do for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
block_table[row] = {}
for x = 1, self.width do for x = 1, self.width do
block_table[row][x] = {
skin = self.grid[row][x].skin,
colour = self.grid[row][x].colour,
}
self.grid[row][x] = { self.grid[row][x] = {
skin = self.grid[row][x].skin, skin = self.grid[row][x].skin,
colour = "X" colour = "X"
} }
self.grid_age[row][x] = 0 --self.grid_age[row][x] = 0
end end
end end
end end
return true return block_table
end end
function Grid:clearClearedRows() function Grid:clearClearedRows()
@@ -388,15 +394,16 @@ end
function Grid:draw() function Grid:draw()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if blocks[self.grid[y][x].skin] and
blocks[self.grid[y][x].skin][self.grid[y][x].colour] then
if self.grid_age[y][x] < 2 then if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
else else
if self.grid[y][x].skin == "bone" then if self.grid[y][x].colour == "X" then
love.graphics.setColor(0, 0, 0, 0)
elseif self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
elseif self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
else else
love.graphics.setColor(0.5, 0.5, 0.5, 1) love.graphics.setColor(0.5, 0.5, 0.5, 1)
end end
@@ -427,10 +434,6 @@ end
function Grid:drawOutline() function Grid:drawOutline()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
end
if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= "X" then if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1) love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
@@ -459,7 +462,7 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
opacity = 1 - self.grid_age[y][x] / 15 opacity = 0
elseif garbage_opacity_function and self.grid[y][x].colour == "A" then elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
opacity = garbage_opacity_function(self.grid_age[y][x]) opacity = garbage_opacity_function(self.grid_age[y][x])
else else
@@ -506,7 +509,7 @@ function Grid:drawCustom(colour_function, gamestate)
if block ~= empty then if block ~= empty then
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x]) local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
A = 1 - self.grid_age[y][x] / 15 A = 0
end end
love.graphics.setColor(R, G, B, A) love.graphics.setColor(R, G, B, A)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)

View File

@@ -104,9 +104,8 @@ function Piece:dropToBottom(grid)
self:dropSquares(math.huge, grid) self:dropSquares(math.huge, grid)
self.gravity = 0 self.gravity = 0
if self.position.y > piece_y then if self.position.y > piece_y then
-- if it got dropped any, also reset lock delay
if self.ghost == false then playSE("bottom") end if self.ghost == false then playSE("bottom") end
self.lock_delay = 0 -- self.lock_delay = 0
end end
return self return self
end end
@@ -118,19 +117,36 @@ function Piece:lockIfBottomed(grid)
return self return self
end end
function Piece:addGravity(gravity, grid) function Piece:addGravity(gravity, grid, classic_lock)
local new_gravity = self.gravity + gravity local new_gravity = self.gravity + gravity
if self:isDropBlocked(grid) then if self:isDropBlocked(grid) then
self.gravity = math.min(1, new_gravity) if classic_lock then
self.lock_delay = self.lock_delay + 1 self.gravity = math.min(1, new_gravity)
else else
local dropped_squares = math.floor(new_gravity) self.gravity = 0
local new_frac_gravity = new_gravity - dropped_squares self.lock_delay = self.lock_delay + 1
self.gravity = new_frac_gravity
self:dropSquares(dropped_squares, grid)
if self:isDropBlocked(grid) then
playSE("bottom")
end end
elseif not (
self:isMoveBlocked(grid, { x=0, y=-1 }) and gravity < 0
) then
local dropped_squares = math.floor(math.abs(new_gravity))
if gravity >= 0 then
local new_frac_gravity = new_gravity - dropped_squares
self.gravity = new_frac_gravity
self:dropSquares(dropped_squares, grid)
if self:isDropBlocked(grid) then
playSE("bottom")
end
else
local new_frac_gravity = new_gravity + dropped_squares
self.gravity = new_frac_gravity
self:moveInGrid({ x=0, y=-1 }, dropped_squares, grid)
if self:isMoveBlocked(grid, { x=0, y=-1 }) then
playSE("bottom")
end
end
else
self.gravity = 0
end end
return self return self
end end

View File

@@ -1,138 +1,24 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode' local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece' local MarathonA2Game = require 'tetris.modes.marathon_a2'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls' local BigA2Game = MarathonA2Game:extend()
local MarathonA2Game = GameMode:extend() BigA2Game.name = "Big A2"
BigA2Game.hash = "BigA2"
BigA2Game.tagline = "Big blocks in the most celebrated TGM mode!"
MarathonA2Game.name = "Big A2" function BigA2Game:new()
MarathonA2Game.hash = "BigA2" BigA2Game.super:new()
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.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 end
function MarathonA2Game:getARE() function BigA2Game:updateScore(level, drop_bonus, cleared_lines)
if self.level < 700 then return 27 cleared_lines = cleared_lines / 2
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 if not self.clear then
cleared_lines = cleared_lines / 2
self:updateGrade(cleared_lines) self:updateGrade(cleared_lines)
if cleared_lines >= 4 then
self.tetris_count = self.tetris_count + 1
end
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end if 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.combo = self.combo + (cleared_lines - 1) * 2
@@ -144,164 +30,21 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
self.combo = 1 self.combo = 1
end end
self.drop_bonus = 0 self.drop_bonus = 0
else self.lines = self.lines + cleared_lines end
end
function BigA2Game:onLineClear(cleared_row_count)
cleared_row_count = cleared_row_count / 2
self:updateSectionTimes(self.level, self.level + cleared_row_count)
self.level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 and not self.clear then
self.clear = true
self.grid:clear()
if self:qualifiesForMRoll() then self.grade = 32 end
self.roll_frames = -150
end end
self.lock_drop = self.level >= 900
self.lock_hard_drop = self.level >= 900
end end
local grade_point_bonuses = { return BigA2Game
{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

View File

@@ -45,6 +45,7 @@ function GameMode:new(secret_inputs)
self.enable_hard_drop = true self.enable_hard_drop = true
self.next_queue_length = 1 self.next_queue_length = 1
self.additive_gravity = true self.additive_gravity = true
self.classic_lock = false
self.draw_section_times = false self.draw_section_times = false
self.draw_secondary_section_times = false self.draw_secondary_section_times = false
self.big_mode = false self.big_mode = false
@@ -63,6 +64,8 @@ function GameMode:new(secret_inputs)
self.hard_drop_locked = false self.hard_drop_locked = false
self.lock_on_soft_drop = false self.lock_on_soft_drop = false
self.lock_on_hard_drop = false self.lock_on_hard_drop = false
self.cleared_block_table = {}
self.last_lcd = 0
self.used_randomizer = nil self.used_randomizer = nil
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
@@ -93,9 +96,8 @@ function GameMode:getSkin()
return "2tie" return "2tie"
end end
function GameMode:initialize(ruleset, secret_inputs) function GameMode:initialize(ruleset)
-- generate next queue -- generate next queue
self:new(secret_inputs)
self.used_randomizer = ( self.used_randomizer = (
ruleset.pieces == self.randomizer.possible_pieces and ruleset.pieces == self.randomizer.possible_pieces and
self.randomizer or self.randomizer or
@@ -105,6 +107,7 @@ function GameMode:initialize(ruleset, secret_inputs)
BagRandomizer(ruleset.pieces) BagRandomizer(ruleset.pieces)
) )
) )
self.ruleset = ruleset
for i = 1, math.max(self.next_queue_length, 1) do for i = 1, math.max(self.next_queue_length, 1) do
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
@@ -134,31 +137,13 @@ function GameMode:update(inputs, ruleset)
self:chargeDAS(inputs, self:getDasLimit(), self:getARR()) self:chargeDAS(inputs, self:getDasLimit(), self:getARR())
-- set attempt flags -- set attempt flags
if inputs["left"] or inputs["right"] then if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece, self.grid) end
self:onAttemptPieceMove(self.piece) if (
if self.immobile_spin_bonus and self.piece ~= nil then
if not self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
not self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) then
self.piece.spin = false
end
end
end
if
inputs["rotate_left"] or inputs["rotate_right"] or inputs["rotate_left"] or inputs["rotate_right"] or
inputs["rotate_left2"] or inputs["rotate_right2"] or inputs["rotate_left2"] or inputs["rotate_right2"] or
inputs["rotate_180"] inputs["rotate_180"]
then ) then
self:onAttemptPieceRotate(self.piece) self:onAttemptPieceRotate(self.piece, self.grid)
if self.immobile_spin_bonus and self.piece ~= nil then
if self.piece:isDropBlocked(self.grid) and
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=0, y=-1 }) then
self.piece.spin = true
else
self.piece.spin = false
end
end
end end
if self.piece == nil then if self.piece == nil then
@@ -177,26 +162,32 @@ function GameMode:update(inputs, ruleset)
return return
end end
if self.lock_drop and inputs["down"] ~= true then if (self.lock_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["down"] ~= true then
self.drop_locked = false self.drop_locked = false
end end
if self.lock_hard_drop and inputs["up"] ~= true then if (self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["up"] ~= true then
self.hard_drop_locked = false self.hard_drop_locked = false
end end
-- diff vars to use in checks -- diff vars to use in checks
local piece_y = self.piece.position.y local piece_y = self.piece.position.y
local piece_x = self.piece.position.x
local piece_rot = self.piece.rotation local piece_rot = self.piece.rotation
ruleset:processPiece( ruleset:processPiece(
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs, inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
self.move, self:getLockDelay(), self:getDropSpeed(), self.move, self:getLockDelay(), self:getDropSpeed(),
self.drop_locked, self.hard_drop_locked, self.drop_locked, self.hard_drop_locked,
self.enable_hard_drop, self.additive_gravity self.enable_hard_drop, self.additive_gravity, self.classic_lock
) )
local piece_dy = self.piece.position.y - piece_y local piece_dy = self.piece.position.y - piece_y
local piece_dx = self.piece.position.x - piece_x
local piece_drot = self.piece.rotation - piece_rot local piece_drot = self.piece.rotation - piece_rot
-- das cut -- das cut
@@ -208,10 +199,20 @@ function GameMode:update(inputs, ruleset)
inputs.rotate_180 inputs.rotate_180
)) ))
) then ) then
self.das.frames = math.max( self:dasCut()
self.das.frames - self:getDasCutDelay(), end
-(self:getDasCutDelay() + 1)
) if (piece_dx ~= 0) then
self.piece.last_rotated = false
self:onPieceMove(self.piece, self.grid, piece_dx)
end
if (piece_drot ~= 0) then
self.piece.last_rotated = true
self:onPieceRotate(self.piece, self.grid, piece_drot)
end
if (piece_dy ~= 0) then
self.piece.last_rotated = false
self:onPieceDrop(self.piece, self.grid, piece_dy)
end end
if inputs["up"] == true and if inputs["up"] == true and
@@ -225,7 +226,12 @@ function GameMode:update(inputs, ruleset)
end end
if inputs["down"] == true then if inputs["down"] == true then
self:onSoftDrop(piece_dy) if not (
self.piece:isDropBlocked(self.grid) and
piece_drot ~= 0
) then
self:onSoftDrop(piece_dy)
end
if self.piece:isDropBlocked(self.grid) and if self.piece:isDropBlocked(self.grid) and
not self.drop_locked and not self.drop_locked and
self.lock_on_soft_drop self.lock_on_soft_drop
@@ -235,7 +241,19 @@ function GameMode:update(inputs, ruleset)
end end
if self.piece.locked == true then if self.piece.locked == true then
-- spin detection, immobile only for now
if self.immobile_spin_bonus and (
self.piece:isDropBlocked(self.grid) and
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=0, y=-1 })
) then
self.piece.spin = true
end
self.grid:applyPiece(self.piece) self.grid:applyPiece(self.piece)
-- mark squares (can be overridden)
if self.square_mode then if self.square_mode then
self.squares = self.squares + self.grid:markSquares() self.squares = self.squares + self.grid:markSquares()
end end
@@ -244,7 +262,7 @@ function GameMode:update(inputs, ruleset)
self:onPieceLock(self.piece, cleared_row_count) self:onPieceLock(self.piece, cleared_row_count)
self:updateScore(self.level, self.drop_bonus, cleared_row_count) self:updateScore(self.level, self.drop_bonus, cleared_row_count)
self.grid:markClearedRows() self.cleared_block_table = self.grid:markClearedRows()
self.piece = nil self.piece = nil
if self.enable_hold then if self.enable_hold then
self.held = false self.held = false
@@ -253,11 +271,13 @@ function GameMode:update(inputs, ruleset)
if cleared_row_count > 0 then if cleared_row_count > 0 then
playSE("erase") playSE("erase")
self.lcd = self:getLineClearDelay() self.lcd = self:getLineClearDelay()
self.last_lcd = self.lcd
self.are = ( self.are = (
ruleset.are and self:getLineARE() or 0 ruleset.are and self:getLineARE() or 0
) )
if self.lcd == 0 then if self.lcd == 0 then
self.grid:clearClearedRows() self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
@@ -287,13 +307,17 @@ end
-- event functions -- event functions
function GameMode:whilePieceActive() end function GameMode:whilePieceActive() end
function GameMode:onAttemptPieceMove(piece) end function GameMode:onAttemptPieceMove(piece, grid) end
function GameMode:onAttemptPieceRotate(piece) end function GameMode:onAttemptPieceRotate(piece, grid) end
function GameMode:onPieceMove(piece, grid, dx) end
function GameMode:onPieceRotate(piece, grid, drot) end
function GameMode:onPieceDrop(piece, grid, dy) end
function GameMode:onPieceLock(piece, cleared_row_count) function GameMode:onPieceLock(piece, cleared_row_count)
playSE("lock") playSE("lock")
end end
function GameMode:onLineClear(cleared_row_count) end function GameMode:onLineClear(cleared_row_count) end
function GameMode:afterLineClear(cleared_row_count) end
function GameMode:onPieceEnter() end function GameMode:onPieceEnter() end
function GameMode:onHold() end function GameMode:onHold() end
@@ -319,6 +343,8 @@ function GameMode:onGameComplete()
self:onGameOver() self:onGameOver()
end end
function GameMode:onExit() end
-- DAS functions -- DAS functions
function GameMode:startRightDAS() function GameMode:startRightDAS()
@@ -359,7 +385,7 @@ function GameMode:stopDAS()
end end
function GameMode:chargeDAS(inputs) function GameMode:chargeDAS(inputs)
if config["das_last_key"] then if config.gamesettings.das_last_key == 2 then
if inputs["right"] == true and self.das.direction ~= "right" and not self.prev_inputs["right"] then if inputs["right"] == true and self.das.direction ~= "right" and not self.prev_inputs["right"] then
self:startRightDAS() self:startRightDAS()
elseif inputs["left"] == true and self.das.direction ~= "left" and not self.prev_inputs["left"] then elseif inputs["left"] == true and self.das.direction ~= "left" and not self.prev_inputs["left"] then
@@ -382,6 +408,13 @@ function GameMode:chargeDAS(inputs)
end end
end end
function GameMode:dasCut()
self.das.frames = math.max(
self.das.frames - self:getDasCutDelay(),
-(self:getDasCutDelay() + 1)
)
end
function GameMode:areCancel(inputs, ruleset) function GameMode:areCancel(inputs, ruleset)
if ruleset.are_cancel and self.piece_hard_dropped and if ruleset.are_cancel and self.piece_hard_dropped and
not self.prev_inputs.up and not self.prev_inputs.up and
@@ -425,7 +458,9 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
self.lcd = self.lcd - 1 self.lcd = self.lcd - 1
self:areCancel(inputs, ruleset) self:areCancel(inputs, ruleset)
if self.lcd == 0 then if self.lcd == 0 then
local cleared_row_count = self.grid:getClearedRowCount()
self.grid:clearClearedRows() self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
playSE("fall") playSE("fall")
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
@@ -448,7 +483,7 @@ end
function GameMode:initializeOrHold(inputs, ruleset) function GameMode:initializeOrHold(inputs, ruleset)
if ( if (
self.frames == 0 or (ruleset.are and self:getARE() ~= 0) and self.ihs or false (self.frames == 0 or (ruleset.are and self:getARE() ~= 0)) and self.ihs or false
) and self.enable_hold and inputs["hold"] == true then ) and self.enable_hold and inputs["hold"] == true then
self:hold(inputs, ruleset, true) self:hold(inputs, ruleset, true)
else else
@@ -504,19 +539,31 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
end end
if self.buffer_hard_drop then if self.buffer_hard_drop then
self.buffer_hard_drop = false self.buffer_hard_drop = false
local above_field = (
(config.gamesettings.spawn_positions == 1 and
ruleset.spawn_above_field) or
config.gamesettings.spawn_positions == 3
)
self:onHardDrop(self.piece.position.y - ( self:onHardDrop(self.piece.position.y - (
self.big_mode and self.piece.big and
ruleset.big_spawn_positions[self.piece.shape].y or ruleset.big_spawn_positions[self.piece.shape].y or
ruleset.spawn_positions[self.piece.shape].y) ruleset.spawn_positions[self.piece.shape].y) +
(above_field and ruleset:getAboveFieldOffset(
piece_data.shape, piece_data.orientation
) or 0)
) )
end end
if self.buffer_soft_drop then if self.buffer_soft_drop then
self.buffer_soft_drop = false self.buffer_soft_drop = false
end end
if self.lock_drop then if self.lock_drop or (
not ruleset.are or self:getARE() == 0
) then
self.drop_locked = true self.drop_locked = true
end end
if self.lock_hard_drop then if self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
) then
self.hard_drop_locked = true self.hard_drop_locked = true
end end
if generate_next_piece == nil then if generate_next_piece == nil then
@@ -536,14 +583,87 @@ function GameMode:getHighScoreData()
} }
end end
function GameMode:animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self.last_lcd),
skin, colour,
48 + x * 16, y * 16
}
end
function GameMode:drawLineClearAnimation()
-- animation function
-- params: block x, y, skin, colour
-- returns: table with RGBA, skin, colour, x, y
-- Fadeout (default)
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self.last_lcd),
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- Flash
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
self.lcd % 6 < 3 and 1 or 0.25,
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- TGM1 pop-out
--[[
function animation(x, y, skin, colour)
local p = 0.5
local l = (
(self.last_lcd - self.lcd) / self.last_lcd
)
local dx = l * (x - (1 + self.grid.width) / 2)
local dy = l * (y - (1 + self.grid.height) / 2)
return {
1, 1, 1, 1, skin, colour,
48 + (x + dx) * 16,
(y + dy) * 16 + (464 / (p - 1)) * l * (p - l)
}
end
--]]
for y, row in pairs(self.cleared_block_table) do
for x, block in pairs(row) do
local animation_table = self:animation(x, y, block.skin, block.colour)
love.graphics.setColor(
animation_table[1], animation_table[2],
animation_table[3], animation_table[4]
)
love.graphics.draw(
blocks[animation_table[5]][animation_table[6]],
animation_table[7], animation_table[8]
)
end
end
end
function GameMode:drawPiece() function GameMode:drawPiece()
if self.piece ~= nil then if self.piece ~= nil then
self.piece:draw( local b = (
1, self.classic_lock and
self:getLockDelay() == 0 and 1 or (
(0.25 + 0.75 * math.max(1 - self.piece.gravity, 1 - (self.piece.lock_delay / self:getLockDelay()))), self.piece:isDropBlocked(self.grid) and
self.grid 1 - self.piece.gravity or 1
) or
1 - (self.piece.lock_delay / self:getLockDelay())
) )
self.piece:draw(1, 0.25 + 0.75 * b, self.grid)
end end
end end
@@ -556,11 +676,16 @@ 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] local colourscheme
if ruleset.pieces == 7 then
colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
else
colourscheme = ruleset.colourscheme
end
function drawPiece(piece, skin, offsets, pos_x, pos_y) 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 = offset.x + ruleset.draw_offsets[piece].x + ruleset.spawn_positions[piece].x local x = offset.x + ruleset:getDrawOffset(piece, rotation).x + ruleset.spawn_positions[piece].x
local y = offset.y + ruleset.draw_offsets[piece].y + 4.7 local y = offset.y + ruleset:getDrawOffset(piece, rotation).y + 4.7
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16) love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
end end
end end
@@ -676,7 +801,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
end end
end end
function GameMode:drawSectionTimesWithSplits(current_section) function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
section_limit = section_limit or math.huge
local section_x = 440 local section_x = 440
local split_x = 530 local split_x = 530
@@ -684,14 +811,18 @@ function GameMode:drawSectionTimesWithSplits(current_section)
for section, time in pairs(self.section_times) do for section, time in pairs(self.section_times) do
if section > 0 then if section > 0 then
love.graphics.setColor(self:sectionColourFunction(section))
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left") love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
love.graphics.setColor(1, 1, 1, 1)
split_time = split_time + time split_time = split_time + time
love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left") love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left")
end end
end end
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left") if (current_section <= section_limit) then
love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left") love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left")
end
end end
function GameMode:drawCustom() end function GameMode:drawCustom() end

View File

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

155
tetris/modes/race_40.lua Normal file
View File

@@ -0,0 +1,155 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
local Race40Game = GameMode:extend()
Race40Game.name = "Race 40"
Race40Game.hash = "Race40"
Race40Game.tagline = "How fast can you clear 40 lines?"
function Race40Game:new()
Race40Game.super:new()
self.lines = 0
self.line_goal = 40
self.pieces = 0
self.randomizer = Bag7Randomiser()
self.roll_frames = 0
self.SGnames = {
[0] = "",
"9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM"
}
self.upstacked = false
self.lock_drop = true
self.lock_hard_drop = true
self.instant_hard_drop = true
self.instant_soft_drop = false
self.enable_hold = true
self.next_queue_length = 6
end
function Race40Game:getDropSpeed()
return 20
end
function Race40Game:getARR()
return config.arr
end
function Race40Game:getARE()
return 0
end
function Race40Game:getLineARE()
return self:getARE()
end
function Race40Game:getDasLimit()
return config.das
end
function Race40Game:getLineClearDelay()
return 0
end
function Race40Game:getLockDelay()
return 30
end
function Race40Game:getGravity()
return 1/64
end
function Race40Game:getDasCutDelay()
return config.dcd
end
function Race40Game:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 150 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function Race40Game:onPieceLock()
self.super:onPieceLock()
self.pieces = self.pieces + 1
end
function Race40Game:onLineClear(cleared_row_count)
if not self.clear then
self.lines = self.lines + cleared_row_count
if self.lines >= self.line_goal then
self.clear = true
end
end
end
function Race40Game:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function Race40Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function Race40Game:getSecretGrade(sg)
if sg == 19 then self.upstacked = true end
if self.upstacked then return self.SGnames[14 + math.floor((20 - sg) / 4)]
else return self.SGnames[math.floor((sg / 19) * 14)] end
end
function Race40Game:drawScoringInfo()
Race40Game.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("LINES", text_x, 320, 40, "left")
love.graphics.printf("line/min", text_x, 160, 80, "left")
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
local sg = self.grid:checkSecretGrade()
if sg >= 7 or self.upstacked then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
if sg >= 7 or self.upstacked then
love.graphics.printf(self:getSecretGrade(sg), 240, 450, 180, "left")
end
love.graphics.setFont(font_3x5_4)
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
end
function Race40Game:getBackground()
return 2
end
return Race40Game

View File

@@ -1,585 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local SakuraRandomizer = require 'tetris.randomizers.sakura'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SakuraGame = GameMode:extend()
SakuraGame.name = "Sakura A3"
SakuraGame.hash = "SakuraA3"
SakuraGame.tagline = "Clear away the Gem Blocks as fast as possible!"
local b = {
["r"] = { skin = "2tie", colour = "R" },
["o"] = { skin = "2tie", colour = "O" },
["y"] = { skin = "2tie", colour = "Y" },
["g"] = { skin = "2tie", colour = "G" },
["c"] = { skin = "2tie", colour = "C" },
["b"] = { skin = "2tie", colour = "B" },
["m"] = { skin = "2tie", colour = "M" },
["R"] = { skin = "gem", colour = "R" },
["O"] = { skin = "gem", colour = "O" },
["Y"] = { skin = "gem", colour = "Y" },
["G"] = { skin = "gem", colour = "G" },
["C"] = { skin = "gem", colour = "C" },
["B"] = { skin = "gem", colour = "B" },
["M"] = { skin = "gem", colour = "M" },
}
local effects = {
[4] = "mirror",
[8] = "xray",
[12] = "color",
[13] = "mirror",
[16] = "roll",
[23] = "big"
}
local maps = {
[1] = {
[22] = {nil, nil, b.O, b.R, nil, nil, b.M, b.m, nil, nil},
[23] = {nil, b.G, b.c, b.c, b.c, b.c, b.c, b.c, b.Y, nil},
[24] = {nil, b.C, b.y, b.y, b.y, b.y, b.y, b.y, b.B, nil},
},
[2] = {
[20] = {nil, nil, nil, nil, b.G, b.b, b.b, b.M, nil, nil},
[21] = {nil, nil, nil, nil, b.c, b.c, b.c, b.c, nil, nil},
[22] = {nil, nil, nil, nil, nil, b.R, b.Y, b.O, nil, nil},
[23] = {nil, b.B, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil},
[24] = {nil, b.b, b.b, b.b, b.b, b.b, b.b, b.b, b.C, nil},
},
[3] = {
[20] = {nil, nil, nil, b.R, b.m, b.o, b.M, nil, nil, nil},
[21] = {nil, nil, nil, nil, b.o, b.O, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
[23] = {nil, b.m, b.o, b.m, b.o, b.m, b.o, b.m, b.o, nil},
[24] = {nil, b.B, b.m, b.o, b.m, b.o, b.m, b.o, b.C, nil},
},
[4] = {
[21] = {nil, nil, b.O, b.g, b.g, b.g, b.g, nil, nil, nil},
[22] = {nil, nil, nil, b.R, b.M, b.b, b.b, nil, nil, nil},
[23] = {b.G, nil, b.Y, b.g, b.g, b.g, b.g, b.g, nil, nil},
[24] = {b.b, b.C, b.b, b.b, b.b, b.b, b.b, b.b, b.B, nil},
},
[5] = {
[16] = {nil, b.B, b.c, b.y, b.c, b.G, b.c, b.y, b.C, nil},
[22] = {nil, nil, b.c, b.y, b.c, b.y, b.c, b.y, nil, nil},
[23] = {nil, b.O, b.y, b.c, b.y, b.c, b.y, b.c, b.Y, nil},
[24] = {nil, b.R, b.c, b.y, b.c, b.y, b.c, b.y, b.M, nil},
},
[6] = {
[21] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
[22] = {nil, nil, b.R, nil, b.b, b.y, nil, b.M, nil, nil},
[23] = {nil, nil, nil, nil, b.y, b.b, nil, nil, nil, nil},
[24] = {nil, b.G, b.y, b.b, b.C, b.y, b.b, b.y, b.B, nil},
},
[7] = {
[20] = {nil, b.C, b.G, nil, b.r, b.g, b.r, b.g, nil, nil},
[21] = {nil, nil, nil, nil, b.R, b.M, b.g, b.r, nil, nil},
[22] = {b.r, nil, nil, nil, b.r, b.g, b.O, b.Y, nil, nil},
[23] = {b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.r, nil, nil},
[24] = {b.r, b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.B, nil},
},
[8] = {
[15] = {nil, nil, nil, b.B, b.m, b.m, b.m, b.m, b.m, b.C},
[16] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
[17] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
[18] = {nil, b.Y, b.y, b.y, b.y, b.y, b.y, b.y, b.y, b.G},
[21] = {b.b, b.b, b.b, b.b, b.b, b.b, b.O, nil, nil, nil},
[22] = {b.b, nil, nil, nil, nil, nil, nil, nil, nil, nil},
[23] = {b.M, nil, nil, nil, nil, nil, nil, nil, nil, nil},
[24] = {b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.R, nil},
},
[9] = {
[18] = {nil, nil, nil, b.Y, b.m, b.m, b.m, b.m, nil, nil},
[19] = {nil, nil, nil, b.c, b.c, b.c, b.c, b.G, nil, nil},
[20] = {b.m, b.m, b.m, b.O, b.M, b.R, nil, nil, nil, nil},
[21] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil, nil},
[22] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, nil, nil},
[23] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.C, nil},
[24] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.B, nil},
},
[10] = {
[18] = {nil, nil, nil, b.C, b.g, b.g, b.B, nil, nil, nil},
[19] = {nil, nil, b.G, b.g, b.g, b.g, b.g, b.Y, nil, nil},
[20] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.g, b.O, nil},
[21] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.c, nil, b.o, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.R, b.o, b.o, nil, nil, nil},
},
[11] = {
[18] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[19] = {nil, nil, nil, nil, b.R, nil, nil, nil, nil, nil},
[20] = {nil, nil, nil, nil, b.r, b.O, nil, nil, nil, nil},
[21] = {nil, nil, nil, nil, nil, b.M, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
[24] = {b.C, b.g, b.g, nil, b.o, b.o, nil, b.g, b.g, b.B},
},
[12] = {
[21] = {nil, nil, nil, nil, nil, nil, nil, b.g, b.g, b.Y},
[22] = {nil, nil, b.r, b.G, b.r, nil, nil, nil, b.O, b.g},
[23] = {nil, b.r, b.C, b.r, b.B, b.r, nil, nil, nil, b.M},
[24] = {b.r, b.r, b.r, b.R, b.r, b.r, b.r, nil, nil, nil},
},
[13] = {
[20] = {b.c, nil, nil, nil, nil, nil, nil, nil, nil, b.B},
[21] = {b.c, b.c, nil, nil, nil, nil, nil, nil, b.C, b.c},
[22] = {b.c, b.c, b.c, nil, nil, nil, nil, b.G, b.c, b.c},
[23] = {b.b, b.b, b.b, b.b, nil, nil, b.Y, b.b, b.b, b.b},
[24] = {nil, b.M, b.b, b.b, b.b, b.O, b.b, b.b, b.R, nil},
},
[14] = {
[20] = {nil, nil, nil, b.y, b.r, b.y, nil, nil, nil, nil},
[21] = {b.R, nil, nil, b.Y, b.y, b.r, b.G, nil, nil, nil},
[22] = {nil, nil, nil, b.y, b.r, b.y, b.r, b.y, b.r, b.B},
[23] = {nil, nil, nil, nil, nil, nil, nil, b.O, b.y, b.r},
[24] = {nil, nil, nil, nil, nil, nil, b.M, b.y, b.r, b.C},
},
[15] = {
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[18] = {nil, nil, b.b, b.y, b.b, b.b, b.y, b.b, nil, nil},
[19] = {nil, nil, nil, b.y, b.b, b.b, b.y, nil, nil, nil},
[20] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.M, b.R, nil, nil, nil, nil},
[23] = {nil, nil, nil, b.G, b.y, b.y, b.C, nil, nil, nil},
[24] = {nil, nil, b.B, b.y, b.y, b.y, b.y, b.y, nil, nil},
},
[16] = {
[18] = {nil, nil, b.O, nil, nil, nil, nil, b.B, nil, nil},
[19] = {nil, nil, b.c, nil, nil, b.G, nil, b.c, nil, nil},
[20] = {nil, nil, b.c, nil, b.C, b.R, nil, b.c, nil, nil},
[21] = {nil, nil, b.c, nil, nil, nil, nil, b.c, nil, nil},
[22] = {nil, nil, b.Y, b.c, b.c, b.c, b.c, b.M, nil, nil},
},
[17] = {
[15] = {b.O, nil, nil, b.g, nil, nil, b.m, nil, nil, b.Y},
[16] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[17] = {nil, nil, nil, b.g, nil, nil, b.R, nil, nil, nil},
[18] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[19] = {nil, nil, nil, b.M, nil, nil, b.m, nil, nil, nil},
[20] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[21] = {nil, nil, nil, b.g, nil, nil, b.G, nil, nil, nil},
[22] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[23] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[24] = {nil, b.m, b.m, b.m, b.B, b.C, b.g, b.g, b.g, nil},
},
[18] = {
[19] = {nil, nil, nil, b.y, b.B, b.y, b.y, nil, nil, nil},
[20] = {nil, nil, b.y, nil, nil, nil, nil, b.y, nil, nil},
[21] = {nil, b.o, nil, nil, b.C, b.R, nil, nil, b.O, nil},
[22] = {nil, b.M, nil, nil, b.Y, b.G, nil, nil, b.o, nil},
[23] = {nil, b.r, b.o, nil, nil, nil, nil, b.o, b.r, nil},
[24] = {nil, b.r, b.r, b.o, b.o, b.o, b.o, b.r, b.r, nil},
},
[19] = {
[15] = {nil, nil, nil, nil, nil, nil, b.O, nil, nil, nil},
[16] = {nil, nil, nil, nil, nil, b.o, nil, nil, nil, nil},
[17] = {nil, nil, nil, nil, b.o, b.r, b.o, nil, nil, nil},
[18] = {nil, b.o, nil, nil, b.o, b.r, b.r, b.o, nil, nil},
[19] = {nil, b.o, b.o, nil, nil, b.R, b.r, b.r, nil, nil},
[20] = {nil, nil, b.M, b.r, nil, b.r, b.r, b.r, b.r, nil},
[21] = {nil, nil, b.r, b.r, b.r, b.r, b.y, b.G, b.o, b.o},
[22] = {nil, b.r, b.o, b.y, b.Y, b.y, b.y, b.y, b.y, b.o},
[23] = {nil, b.o, b.o, b.y, b.y, b.c, b.c, b.c, b.c, b.C},
[24] = {nil, nil, b.o, b.y, b.b, b.b, b.B, b.b, b.b, b.b},
},
[20] = {
[20] = {nil, nil, b.B, b.b, b.b, b.b, b.b, b.b, nil, nil},
[21] = {b.c, nil, nil, b.C, b.c, b.c, b.c, nil, nil, b.c},
[22] = {b.g, b.g, nil, nil, b.G, b.g, nil, nil, b.g, b.g},
[23] = {b.y, b.y, b.o, nil, nil, nil, nil, b.Y, b.y, b.y},
[24] = {b.r, b.r, b.r, b.R, nil, nil, b.M, b.r, b.r, b.r},
},
[21] = {
[16] = {nil, nil, b.g, b.g, b.g, b.g, b.g, nil, nil, nil},
[17] = {nil, b.g, b.g, b.g, b.g, b.g, b.g, b.B, b.g, nil},
[18] = {b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.C, nil},
[19] = {b.g, nil, nil, b.g, b.o, b.o, b.g, nil, nil, b.g},
[20] = {nil, b.R, nil, b.g, b.o, b.o, nil, b.M, nil, b.G},
[21] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.Y, b.O, nil, nil, nil, nil},
},
[22] = {
[15] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[16] = {nil, b.b, b.O, b.b, nil, nil, b.b, b.Y, b.b, nil},
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[20] = {nil, nil, nil, nil, b.R, b.M, nil, nil, nil, nil},
[23] = {nil, nil, nil, b.b, b.C, b.G, b.b, nil, nil, nil},
[24] = {nil, nil, nil, b.b, b.b, b.B, b.b, nil, nil, nil},
},
[23] = {
[13] = {nil, nil, nil, nil, nil, nil, nil, nil, b.c, b.m},
[14] = {nil, nil, nil, nil, nil, nil, nil, nil, b.y, b.g},
[15] = {b.G, b.B, nil, nil, nil, nil, nil, nil, nil, nil},
[16] = {b.r, b.O, nil, nil, nil, nil, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.C, b.M, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.R, b.Y, nil, nil, nil, nil},
},
[24] = {
[20] = {b.g, b.g, b.g, b.g, b.G, nil, nil, nil, nil, nil},
[21] = {nil, nil, nil, nil, nil, b.O, b.y, b.y, b.y, b.Y},
[23] = {b.M, b.r, b.r, b.r, b.R, nil, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, nil, b.M, b.r, b.r, b.r, b.R},
},
[25] = {
[18] = {nil, nil, nil, nil, nil, b.B, nil, nil, nil, nil},
[19] = {nil, nil, nil, b.G, nil, nil, nil, b.C, nil, nil},
[20] = {nil, nil, nil, nil, nil, b.Y, nil, nil, nil, nil},
[21] = {nil, nil, nil, b.M, nil, nil, nil, b.O, nil, nil},
[22] = {nil, nil, nil, nil, nil, b.R, nil, nil, nil, nil},
},
[26] = {
[13] = {nil, nil, nil, nil, b.r, b.r, nil, nil, nil, nil},
[14] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[15] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[16] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[17] = {nil, nil, nil, b.O, b.o, b.o, b.Y, nil, nil, nil},
[18] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
[19] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
[20] = {nil, nil, b.o, nil, nil, b.G, nil, b.o, nil, nil},
[21] = {nil, nil, b.o, nil, b.M, b.R, nil, b.o, nil, nil},
[22] = {nil, nil, b.o, b.b, b.b, b.b, b.B, b.o, nil, nil},
[23] = {nil, nil, b.o, b.C, b.b, b.b, b.b, b.o, nil, nil},
[24] = {nil, nil, b.o, b.o, b.o, b.o, b.o, b.o, nil, nil},
},
[27] = {
[15] = {nil, b.C, b.o, b.g, b.g, b.g, b.g, b.g, b.B, nil},
[16] = {b.g, nil, b.y, b.o, b.g, b.g, b.g, b.g, nil, b.y},
[17] = {b.g, b.g, nil, b.y, b.o, b.g, b.g, nil, b.y, b.o},
[18] = {b.g, b.g, b.g, nil, b.y, b.o, nil, b.y, b.o, b.g},
[19] = {b.g, b.g, b.g, b.o, nil, b.G, b.y, b.o, b.g, b.g},
[20] = {b.g, b.g, b.o, b.o, b.Y, nil, b.o, b.g, b.g, b.g},
[21] = {b.g, b.o, b.y, nil, b.o, b.O, nil, b.g, b.g, b.g},
[22] = {b.o, b.y, nil, b.g, b.g, b.o, b.y, nil, b.g, b.g},
[23] = {b.y, nil, b.g, b.g, b.g, b.g, b.o, b.y, nil, b.g},
[24] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.o, b.R, nil},
},
}
local STAGE_TRANSITION_TIME = 300
function SakuraGame:new(secret_inputs)
self.super:new()
self.randomizer = (
(
secret_inputs.rotate_left and secret_inputs.rotate_right
) and History6RollsRandomizer() or SakuraRandomizer()
)
self.current_map = 1
self.time_limit = 10800
self.cleared_frames = STAGE_TRANSITION_TIME
self.stage_frames = 0
self.time_extend = 0
self.maps_cleared = 0
self.map_20_time = 0
self.stage_pieces = 0
self.grid:applyMap(maps[self.current_map])
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function SakuraGame:checkRequirements()
if self.maps_cleared >= 14 + 2 * (self.current_map - 20) and
self.map_20_time <= frameTime(8,00) - frameTime(0,30) * (self.current_map - 20)
then
return false
end
return true
end
function SakuraGame:getGravity()
if self.level < 8 then return 4/256
elseif self.level < 19 then return 5/256
elseif self.level < 35 then return 6/256
elseif self.level < 40 then return 8/256
elseif self.level < 50 then return 10/256
elseif self.level < 60 then return 12/256
elseif self.level < 70 then return 16/256
elseif self.level < 80 then return 32/256
elseif self.level < 90 then return 48/256
elseif self.level < 100 then return 64/256
elseif self.level < 108 then return 4/256
elseif self.level < 119 then return 5/256
elseif self.level < 125 then return 6/256
elseif self.level < 131 then return 8/256
elseif self.level < 139 then return 12/256
elseif self.level < 149 then return 32/256
elseif self.level < 156 then return 48/256
elseif self.level < 164 then return 80/256
elseif self.level < 174 then return 112/256
elseif self.level < 180 then return 128/256
elseif self.level < 200 then return 144/256
elseif self.level < 212 then return 16/256
elseif self.level < 221 then return 48/256
elseif self.level < 232 then return 80/256
elseif self.level < 244 then return 112/256
elseif self.level < 256 then return 144/256
elseif self.level < 267 then return 176/256
elseif self.level < 277 then return 192/256
elseif self.level < 287 then return 208/256
elseif self.level < 295 then return 224/256
elseif self.level < 300 then return 240/256
else return 20 end
end
function SakuraGame:onLineClear(cleared_row_count)
self.level = self.level + cleared_row_count
for i = 13, 24 do
for j = 1, 10 do
local block = self.grid.grid[i][j]
if block and block.skin == "gem" and block.colour == "X" then
self.time_limit = self.time_limit + 60
end
end
end
end
function SakuraGame:onPieceEnter()
if self.level % 100 ~= 99 and not self.clear and self.stage_frames ~= 0 then
self.level = self.level + 1
end
if effects[self.current_map] == "mirror" and
self.stage_pieces % 3 == 0 and self.stage_pieces ~= 0
then
self.grid:mirror()
end
self.stage_pieces = self.stage_pieces + 1
end
function SakuraGame:advanceOneFrame(inputs, ruleset)
if self.ready_frames == 0 then
if self.lcd > 0 then
if self.stage_frames <= frameTime(0,10) then self.time_extend = 600
elseif self.stage_frames <= frameTime(0,30) then self.time_extend = 300
else self.time_extend = 0 end
end
if not self.grid:hasGemBlocks() or
(self.stage_frames >= 3600 and self.current_map <= 20) then
self.lcd = 0
self.are = 0
if self.stage_frames >= 3600 then self.time_extend = 0 end
self.piece = nil
-- transition to next map
if self.cleared_frames > 0 then
self.cleared_frames = self.cleared_frames - 1
if self.time_extend > 0 then
self.time_limit = self.time_limit + 3
self.time_extend = self.time_extend - 3
end
return false
end
self.hold_queue = nil
if self.current_map > 20 or (self.stage_frames < 3600 and self.current_map <= 20) then self.maps_cleared = self.maps_cleared + 1 end
self.stage_frames = -1
self.level = 0
self.grid:clear()
if (self.current_map == 20) then self.map_20_time = self.frames end
if self.current_map >= 20 and self:checkRequirements() then
self.clear = true
self.completed = true
return false
else
self.current_map = self.current_map + 1
self.big_mode = effects[self.current_map] == "big"
self.ready_frames = 100
self.stage_pieces = 0
self.grid:applyMap(maps[self.current_map])
end
-- this is necessary to fix timer
self.frames = self.frames - 1
self.time_limit = self.time_limit + 1
end
self.frames = self.frames + 1
self.stage_frames = self.stage_frames + 1
self.time_limit = math.max(self.time_limit - 1, 0)
if self.time_limit <= 0 and self.piece == nil then
self.game_over = true
end
if self.piece ~= nil and
effects[self.current_map] == "roll" and
self.stage_pieces % 4 == 0
then
self.piece.colour = "F"
if self.stage_frames % 30 == 0 then
ruleset:attemptRotate(
{[config.gamesettings.world_reverse == 3 or
(ruleset.world and config.gamesettings.world_reverse == 2)
and "rotate_left" or "rotate_right"] = true},
self.piece, self.grid, false
)
end
end
else
self.cleared_frames = STAGE_TRANSITION_TIME
if not self.prev_inputs.hold and inputs.hold then
self.hold_queue = table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
end
return true
end
function SakuraGame:onGameComplete() end
local function colourXRay(game, block, x, y, age)
local r, g, b, a = .5,.5,.5
if ((game.stage_frames/2 - x) % 30 < 1)
or game.stage_frames == 0
or game.cleared_frames ~= STAGE_TRANSITION_TIME
or game.stage_pieces % 2 == 0
then
a = 1
else
a = 1 - age / 4
end
return r, g, b, a, a
end
local function colourColor(game, block, x, y, age)
local r, g, b, a = .5,.5,.5
if game.stage_frames == 0 or game.cleared_frames ~= STAGE_TRANSITION_TIME then
a = 1
else
a = (game.stage_frames/30 + (y + math.abs(x-5.5))/5) % 1
end
return r, g, b, a, 0
end
function SakuraGame:drawGrid()
if effects[self.current_map] == "xray" then
self.grid:drawCustom(colourXRay, self)
elseif effects[self.current_map] == "color" then
self.grid:drawCustom(colourColor, self)
else
self.grid:draw()
end
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece()
end
end
function SakuraGame:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
strTrueValues(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("STAGE", 240, 120, 80, "left")
love.graphics.printf("TIME LIMIT", 240, 180, 80, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
if self.current_map <= 20 then
love.graphics.printf("STAGE LIMIT", 240, 240, 100, "left")
end
if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
end
if self.used_randomizer.history then
love.graphics.printf("RANDOM PIECES ACTIVE!", 240, 150, 200, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.setColor(
(self.time_limit % 4 < 2 and
self.time_limit <= frameTime(0,10) and
self.grid:hasGemBlocks() and
self.time_limit ~= 0 and
self.ready_frames == 0) and
{ 1, 0.3, 0.3, 1 } or
{ 1, 1, 1, 1 }
)
love.graphics.printf(formatTime(self.time_limit), 240, 200, 120, "left")
love.graphics.setColor(1, 1, 1, 1)
if self.current_map <= 20 then
love.graphics.printf(formatTime(3600 - self.stage_frames), 240, 260, 120, "left")
end
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(math.floor((self.level + 100) / 100) * 100, 240, 370, 40, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
love.graphics.printf(self.current_map, 290, 110, 80, "left")
end
function SakuraGame:drawCustom()
love.graphics.setColor(1, 1, 1, 1)
if self.ready_frames ~= 0 and not self.clear then
love.graphics.setFont(font_3x5_4)
love.graphics.printf("STAGE " .. self.current_map, 64, 170, 160, "center")
love.graphics.setFont(font_3x5_3)
if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 64, 270, 160, "center")
end
end
if self.cleared_frames > 0 and
(not self.grid:hasGemBlocks() or
(self.stage_frames >= 3600 and self.current_map <= 20)) then
love.graphics.setFont(font_3x5_2)
love.graphics.printf("TIME LIMIT", 64, 180, 160, "center")
love.graphics.printf("TIME EXTEND", 64, 240, 160, "center")
love.graphics.printf("STAGE TIME", 64, 300, 160, "center")
love.graphics.setFont(font_3x5_3)
love.graphics.printf("STAGE " .. self.current_map, 64, 100, 160, "center")
love.graphics.setColor(
self.cleared_frames % 4 < 2 and
{ 1, 1, 0.3, 1 } or
{ 1, 1, 1, 1 }
)
love.graphics.printf(formatTime(self.time_limit), 64, 200, 160, "center")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(formatTime(self.time_extend), 64, 260, 160, "center")
love.graphics.printf(formatTime(self.stage_frames), 64, 320, 160, "center")
love.graphics.setFont(font_3x5_4)
love.graphics.printf((self.stage_frames >= 3600 and self.current_map <= 20) and "" or "CLEAR!", 64, 130, 160, "center")
end
if self.clear then
love.graphics.setFont(font_3x5_3)
love.graphics.printf("EXCELLENT!", 64, 180, 160, "center")
love.graphics.setFont(font_3x5_2)
if self.current_map ~= 27 then
love.graphics.printf("...but let's go\nbetter next time", 64, 220, 160, "center")
end
end
end
function SakuraGame:getBackground()
return (self.current_map - 1) % 20
end
function SakuraGame:getHighscoreData()
return {
maps = self.maps_cleared,
current_map = self.current_map,
frames = self.frames,
}
end
return SakuraGame

View File

@@ -52,23 +52,14 @@ function StrategyGame:getLineClearDelay()
end end
function StrategyGame:getLockDelay() function StrategyGame:getLockDelay()
if self.level < 500 then return 8 if self.level < 700 then return 8
elseif self.level < 700 then return 6 else return 6 end
else return 4 end
end end
function StrategyGame:getGravity() function StrategyGame:getGravity()
return 20 return 20
end end
function StrategyGame:getNextPiece(ruleset)
return {
skin = "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
function StrategyGame:advanceOneFrame() function StrategyGame:advanceOneFrame()
if self.clear then if self.clear then
self.roll_frames = self.roll_frames + 1 self.roll_frames = self.roll_frames + 1

View File

@@ -94,12 +94,8 @@ function Survival2020Game:getGravity()
return 20 return 20
end end
function Survival2020Game:getNextPiece(ruleset) function Survival2020Game:getSkin()
return { return self.level >= 1000 and "bone" or "2tie"
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end end
function Survival2020Game:hitTorikan(old_level, new_level) function Survival2020Game:hitTorikan(old_level, new_level)

View File

@@ -30,6 +30,11 @@ function SurvivalA2Game:new()
self.lock_hard_drop = true self.lock_hard_drop = true
end end
function SurvivalA2Game:initialize(ruleset)
SurvivalA2Game.super.initialize(self, ruleset)
self.world = ruleset.world
end
function SurvivalA2Game:getARE() function SurvivalA2Game:getARE()
if self.level < 100 then return 18 if self.level < 100 then return 18
elseif self.level < 300 then return 14 elseif self.level < 300 then return 14
@@ -69,7 +74,8 @@ 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 > frameTime(3,25) then local torikan_time = self.world and frameTime(3,55) or frameTime(3,25)
if old_level < 500 and new_level >= 500 and self.frames > torikan_time then
self.level = 500 self.level = 500
return true return true
end end

View File

@@ -15,7 +15,6 @@ SurvivalAXGame.tagline = "Can you clear the time hurdles when the game goes this
function SurvivalAXGame:new() function SurvivalAXGame:new()
SurvivalAXGame.super:new() SurvivalAXGame.super:new()
self.roll_frames = 0
self.randomizer = Bag7NoSZOStartRandomizer() self.randomizer = Bag7NoSZOStartRandomizer()
self.section_time_limit = 3600 self.section_time_limit = 3600
@@ -75,14 +74,7 @@ function SurvivalAXGame:getSection()
end end
function SurvivalAXGame:advanceOneFrame() function SurvivalAXGame:advanceOneFrame()
if self.clear then if self.ready_frames == 0 then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames > 2968 then
self.completed = true
end
elseif self.ready_frames == 0 then
if not self.section_clear then if not self.section_clear then
self.frames = self.frames + 1 self.frames = self.frames + 1
end end
@@ -101,7 +93,7 @@ function SurvivalAXGame:onLineClear(cleared_row_count)
if self.lines == 150 then if self.lines == 150 then
self.grid:clear() self.grid:clear()
self.clear = true self.clear = true
self.roll_frames = -150 self.completed = true
end end
end end
end end

View File

@@ -4,6 +4,7 @@ local BagRandomizer = Randomizer:extend()
function BagRandomizer:new(pieces) function BagRandomizer:new(pieces)
self.bag = {} self.bag = {}
self.possible_pieces = pieces
self.pieces = pieces self.pieces = pieces
for i = 1, self.pieces do for i = 1, self.pieces do
table.insert(self.bag, i) table.insert(self.bag, i)

View File

@@ -28,11 +28,8 @@ end
function History6Rolls35PoolRandomizer:generatePiece() function History6Rolls35PoolRandomizer:generatePiece()
local index, x local index, x
if self.first then if self.first then
local prevent = {"S", "Z", "O"} index = math.random(20)
repeat x = self.pool[index]
index = math.random(#self.pool)
x = self.pool[index]
until not inHistory(x, prevent)
self.first = false self.first = false
else else
for i = 1, 6 do for i = 1, 6 do

View File

@@ -1,5 +1,5 @@
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.arika_ti' local Ruleset = require 'tetris.rulesets.arika_ace2'
local ARS = Ruleset:extend() local ARS = Ruleset:extend()
@@ -19,38 +19,4 @@ ARS.colourscheme = {
ARS.softdrop_lock = false ARS.softdrop_lock = false
ARS.harddrop_lock = true ARS.harddrop_lock = true
function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0
piece.manipulations = 0
end
function ARS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true
end
end
end
function ARS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true
end
end
if piece.floorkick >= 1 then
piece.floorkick = piece.floorkick + 1
end
end
function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@@ -5,12 +5,86 @@ local ARS = Ruleset:extend()
ARS.name = "ACE-ARS2" ARS.name = "ACE-ARS2"
ARS.hash = "ArikaACE2" ARS.hash = "ArikaACE2"
ARS.spawn_above_field = true
function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
-- O doesn't kick
if (piece.shape == "O") then return end
-- center column rule
if (
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
) and (
piece.rotation == 0 or piece.rotation == 2
) then
local offsets = new_piece:getBlockOffsets()
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
for index, offset in pairs(offsets) do
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
if offset.x == 0 then
return
else
break
end
end
end
end
if piece.shape == "I" then
-- special kick rules for I
if (new_piece.rotation == 0 or new_piece.rotation == 2) and
(piece:isMoveBlocked(grid, {x=-1, y=0}) or piece:isMoveBlocked(grid, {x=1, y=0})) then
-- kick right, right2, left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
end
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) then
-- kick up, up2
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
end
end
else
-- kick right, kick left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
elseif piece.shape == "T"
and new_piece.rotation == 0
and piece:isDropBlocked(grid)
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
then
-- T floorkick
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
end
end
end
function ARS:onPieceCreate(piece, grid) function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0
piece.manipulations = 0 piece.manipulations = 0
end end
function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0
end
function ARS:onPieceMove(piece, grid) function ARS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
@@ -31,9 +105,6 @@ function ARS:onPieceRotate(piece, grid)
piece.locked = true piece.locked = true
end end
end end
if piece.floorkick >= 1 then
piece.floorkick = piece.floorkick + 1
end
end end
function ARS:get180RotationValue() return 3 end function ARS:get180RotationValue() return 3 end

View File

@@ -17,17 +17,22 @@ SRS.colourscheme = {
} }
SRS.softdrop_lock = false SRS.softdrop_lock = false
SRS.harddrop_lock = true SRS.harddrop_lock = true
SRS.spawn_above_field = true
SRS.MANIPULATIONS_MAX = 128 SRS.MANIPULATIONS_MAX = 128
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid, upward)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then if upward or piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= self.MANIPULATIONS_MAX then if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
end end
end end
end end
function SRS:canPieceRotate(piece)
return piece.manipulations < self.MANIPULATIONS_MAX
end
return SRS return SRS

View File

@@ -38,35 +38,35 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
(piece:isMoveBlocked(grid, {x=-1, y=0}) or piece:isMoveBlocked(grid, {x=1, y=0})) then (piece:isMoveBlocked(grid, {x=-1, y=0}) or piece:isMoveBlocked(grid, {x=1, y=0})) then
-- kick right, right2, left -- kick right, right2, 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=2, y=0})) then elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid)
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
self:onPieceRotate(piece, grid)
end end
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then
-- kick up, up2 -- kick up, up2
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
piece.floorkick = 1 piece.floorkick = 1
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid)
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
piece.floorkick = 1 piece.floorkick = 1
self:onPieceRotate(piece, grid)
end end
end end
else else
-- kick right, kick left -- kick right, kick left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid)
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
self:onPieceRotate(piece, grid)
elseif piece.shape == "T" elseif piece.shape == "T"
and new_piece.rotation == 0 and new_piece.rotation == 0
and piece.floorkick == 0 and piece.floorkick == 0
@@ -75,8 +75,8 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
then then
-- T floorkick -- T floorkick
piece.floorkick = piece.floorkick + 1 piece.floorkick = piece.floorkick + 1
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
self:onPieceRotate(piece, grid)
end end
end end
@@ -94,7 +94,9 @@ function ARS:onPieceDrop(piece, grid)
end end
function ARS:onPieceRotate(piece, grid) function ARS:onPieceRotate(piece, grid)
if piece.floorkick >= 1 then if piece.floorkick >= 2 and piece:isDropBlocked(grid) then
piece.locked = true
elseif piece.floorkick >= 1 then
piece.floorkick = piece.floorkick + 1 piece.floorkick = piece.floorkick + 1
end end
end end

View File

@@ -384,9 +384,9 @@ function CRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid)
return return
end end
end end

View File

@@ -1,275 +0,0 @@
local Ruleset = require 'tetris.rulesets.ruleset'
local PAIRS = Ruleset:extend()
PAIRS.name = "PAIRS"
PAIRS.hash = "PAIRS"
PAIRS.world = true
PAIRS.spawn_positions = {
[1] = { x=4, y=4 },
[2] = { x=4, y=5 },
[3] = { x=4, y=5 },
[4] = { x=4, y=5 },
[5] = { x=5, y=5 },
[6] = { x=5, y=5 },
[7] = { x=5, y=5 },
[8] = { x=5, y=5 },
[9] = { x=5, y=5 },
[10] = { x=5, y=5 },
[11] = { x=4, y=5 },
[12] = { x=4, y=5 },
[13] = { x=4, y=5 },
[14] = { x=4, y=5 },
[15] = { x=4, y=5 },
[16] = { x=4, y=5 },
[17] = { x=4, y=5 },
[18] = { x=4, y=5 },
}
PAIRS.big_spawn_positions = {
[1] = { x=2, y=2 },
[2] = { x=2, y=3 },
[3] = { x=2, y=3 },
[4] = { x=2, y=3 },
[5] = { x=3, y=3 },
[6] = { x=3, y=3 },
[7] = { x=3, y=3 },
[8] = { x=3, y=3 },
[9] = { x=3, y=3 },
[10] = { x=3, y=3 },
[11] = { x=2, y=3 },
[12] = { x=2, y=3 },
[13] = { x=2, y=3 },
[14] = { x=2, y=3 },
[15] = { x=2, y=3 },
[16] = { x=2, y=3 },
[17] = { x=2, y=3 },
[18] = { x=2, y=3 },
}
PAIRS.draw_offsets = {
[1] = { x=0, y=0 },
[2] = { x=0, y=0 },
[3] = { x=0, y=0 },
[4] = { x=0, y=0 },
[5] = { x=0, y=0 },
[6] = { x=0, y=0 },
[7] = { x=0, y=0 },
[8] = { x=0, y=0 },
[9] = { x=0, y=0 },
[10] = { x=0, y=0 },
[11] = { x=0, y=0 },
[12] = { x=0, y=0 },
[13] = { x=0, y=0 },
[14] = { x=0, y=0 },
[15] = { x=0, y=0 },
[16] = { x=0, y=0 },
[17] = { x=0, y=0 },
[18] = { x=0, y=0 },
}
PAIRS.next_sounds = {
[1] = "I",
[2] = "O",
[3] = "S",
[4] = "Z",
[5] = "L",
[6] = "J",
[7] = "Z",
[8] = "S",
[9] = "J",
[10] = "L",
[11] = "O",
[12] = "O",
[13] = "T",
[14] = "L",
[15] = "J",
[16] = "T",
[17] = "J",
[18] = "I"
}
PAIRS.colourscheme = {
[1] = "R",
[2] = "C",
[3] = "G",
[4] = "M",
[5] = "O",
[6] = "C",
[7] = "G",
[8] = "M",
[9] = "G",
[10] = "M",
[11] = "Y",
[12] = "B",
[13] = "M",
[14] = "O",
[15] = "B",
[16] = "G",
[17] = "C",
[18] = "R"
}
PAIRS.pieces = 18
PAIRS.block_offsets = {
[1]={
{ {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} },
{ {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} },
{ {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} },
{ {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} },
},
[2]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
},
[3]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0}, {x=-1, y=-2} },
},
[4]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0}, {x=1, y=-2} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0}, {x=1, y=-2} },
},
[5]={
{ {x=1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-2, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-3}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[6]={
{ {x=-2, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-3}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=1, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=0}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[7]={
{ {x=-2, y=-1}, {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-3}, {x=0, y=-2}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=0}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=-1, y=-1} },
},
[8]={
{ {x=1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=-2, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-3}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[9]={
{ {x=-1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-2}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=0, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-1}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[10]={
{ {x=0, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-1, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-2}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[11]={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-1}, {x=1, y=-2} },
{ {x=0, y=0}, {x=1, y=-1}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
},
[12]={
{ {x=0, y=0}, {x=1, y=-1}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-1}, {x=1, y=0} },
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-1}, {x=-1, y=-2} },
},
[13]={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=0}, {x=1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=0}, {x=-1, y=-2} },
},
[14]={
{ {x=0, y=-1}, {x=0, y=0}, {x=0, y=-2}, {x=-1, y=-1}, {x=1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=1, y=-1}, {x=-1, y=-1}, {x=0, y=0}, {x=1, y=-2} },
},
[15]={
{ {x=0, y=-1}, {x=0, y=0}, {x=0, y=-2}, {x=-1, y=0}, {x=1, y=-1} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=-2}, {x=0, y=0} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=1, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=-2} },
},
[16]={
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=0}, {x=1, y=0} },
{ {x=-1, y=0}, {x=0, y=-1}, {x=-1, y=-2}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-2}, {x=1, y=-2} },
{ {x=1, y=0}, {x=0, y=-1}, {x=1, y=-2}, {x=-1, y=-1}, {x=1, y=-1} },
},
[17]={
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-2}, {x=1, y=-2}, {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-2}, {x=1, y=0}, {x=-1, y=-2}, {x=1, y=-1}, {x=1, y=-2} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=-1}, {x=1, y=-2} },
},
[18]={
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-2} },
{ {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=-1}, {x=1, y=-1}, {x=1, y=0} },
{ {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
},
}
PAIRS.wallkicks = {
{x=1, y=0}, {x=-1, y=0}, {x=2, y=0}, {x=-2, y=0}, {x=0, y=-1}
}
function PAIRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
if (piece.shape == 2) then return end
local kicks = PAIRS.wallkicks
assert(piece.rotation ~= new_piece.rotation)
for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
return
end
end
end
function PAIRS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y
if y > piece.lowest_y then
piece.lock_delay = 0
piece.lowest_y = y
end
end
end
function PAIRS:onPieceCreate(piece, grid)
piece.lowest_y = -math.huge
end
function PAIRS:onPieceDrop(piece, grid)
self:checkNewLow(piece)
end
function PAIRS:get180RotationValue()
if config.gamesettings.world_reverse == 1 then
return 1
else
return 3
end
end
return PAIRS

View File

@@ -23,6 +23,7 @@ Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false Ruleset.enable_IRS_wallkicks = false
Ruleset.are_cancel = false Ruleset.are_cancel = false
Ruleset.are = true Ruleset.are = true
Ruleset.spawn_above_field = false
Ruleset.next_sounds = { Ruleset.next_sounds = {
I = "I", I = "I",
@@ -34,73 +35,30 @@ Ruleset.next_sounds = {
T = "T" T = "T"
} }
Ruleset.draw_offsets = {
I = { x=0, y=0 },
J = { x=0, y=0 },
L = { x=0, y=0 },
O = { x=0, y=0 },
S = { x=0, y=0 },
T = { x=0, y=0 },
Z = { x=0, y=0 },
}
Ruleset.pieces = 7 Ruleset.pieces = 7
-- Component functions. -- Component functions.
function Ruleset:new() function Ruleset:new(game_mode)
self.game = game_mode
local bones
if config.gamesettings.piece_colour == 1 then if config.gamesettings.piece_colour == 1 then
blocks["bone"] = (not self.world) and bones = self.world and "w" or ""
{
R = love.graphics.newImage("res/img/bone.png"),
O = love.graphics.newImage("res/img/bone.png"),
Y = love.graphics.newImage("res/img/bone.png"),
G = love.graphics.newImage("res/img/bone.png"),
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
} or {
R = love.graphics.newImage("res/img/bonew.png"),
O = love.graphics.newImage("res/img/bonew.png"),
Y = love.graphics.newImage("res/img/bonew.png"),
G = love.graphics.newImage("res/img/bonew.png"),
C = love.graphics.newImage("res/img/bonew.png"),
B = love.graphics.newImage("res/img/bonew.png"),
M = love.graphics.newImage("res/img/bonew.png"),
F = love.graphics.newImage("res/img/bonew.png"),
A = love.graphics.newImage("res/img/bonew.png"),
X = love.graphics.newImage("res/img/bonew.png"),
}
else else
blocks["bone"] = (config.gamesettings.piece_colour == 2) and bones = config.gamesettings.piece_colour == 3 and "w" or ""
{
R = love.graphics.newImage("res/img/bone.png"),
O = love.graphics.newImage("res/img/bone.png"),
Y = love.graphics.newImage("res/img/bone.png"),
G = love.graphics.newImage("res/img/bone.png"),
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
} or {
R = love.graphics.newImage("res/img/bonew.png"),
O = love.graphics.newImage("res/img/bonew.png"),
Y = love.graphics.newImage("res/img/bonew.png"),
G = love.graphics.newImage("res/img/bonew.png"),
C = love.graphics.newImage("res/img/bonew.png"),
B = love.graphics.newImage("res/img/bonew.png"),
M = love.graphics.newImage("res/img/bonew.png"),
F = love.graphics.newImage("res/img/bonew.png"),
A = love.graphics.newImage("res/img/bonew.png"),
X = love.graphics.newImage("res/img/bonew.png"),
}
end end
blocks.bone = {
R = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
O = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
Y = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
G = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
C = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
B = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
M = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
F = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
A = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
X = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
}
end end
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial) function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
@@ -114,7 +72,9 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
local was_drop_blocked = piece:isDropBlocked(grid) local was_drop_blocked = piece:isDropBlocked(grid)
self:attemptRotate(new_inputs, piece, grid, initial) if self:canPieceRotate(piece, grid) then
self:attemptRotate(new_inputs, piece, grid, initial)
end
if not was_drop_blocked and piece:isDropBlocked(grid) then if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom") playSE("bottom")
@@ -159,28 +119,43 @@ function Ruleset:attemptWallkicks(piece, new_piece, rot_dir, grid)
end end
function Ruleset:movePiece(piece, grid, move, instant) function Ruleset:movePiece(piece, grid, move, instant)
local x = piece.position.x if not self:canPieceMove(piece, grid) then return end
local was_drop_blocked = piece:isDropBlocked(grid) local was_drop_blocked = piece:isDropBlocked(grid)
local offset = ({x=0, y=0})
local moves = 0
if move == "left" then if move == "left" then
piece:moveInGrid({x=-1, y=0}, 1, grid, false) offset.x = -1
moves = 1
elseif move == "right" then elseif move == "right" then
piece:moveInGrid({x=1, y=0}, 1, grid, false) offset.x = 1
moves = 1
elseif move == "speedleft" then elseif move == "speedleft" then
piece:moveInGrid({x=-1, y=0}, grid.width, grid, instant) offset.x = -1
moves = grid.width
elseif move == "speedright" then elseif move == "speedright" then
piece:moveInGrid({x=1, y=0}, grid.width, grid, instant) offset.x = 1
moves = grid.width
end end
if piece.position.x ~= x then for i = 1, moves do
self:onPieceMove(piece, grid) local x = piece.position.x
if not was_drop_blocked and piece:isDropBlocked(grid) then if moves ~= 1 then
playSE("bottom") piece:moveInGrid(offset, 1, grid, instant)
else
piece:moveInGrid(offset, 1, grid, false)
end end
if piece.position.x ~= x then
self:onPieceMove(piece, grid)
if piece.locked then break end
end
end
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
end end
end end
function Ruleset:dropPiece( function Ruleset:dropPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked, inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity hard_drop_enabled, additive_gravity, classic_lock
) )
if piece.big then if piece.big then
gravity = gravity / 2 gravity = gravity / 2
@@ -190,32 +165,43 @@ function Ruleset:dropPiece(
local y = piece.position.y local y = piece.position.y
if inputs["down"] == true and drop_locked == false then if inputs["down"] == true and drop_locked == false then
if additive_gravity then if additive_gravity then
piece:addGravity(gravity + drop_speed, grid) piece:addGravity(gravity + drop_speed, grid, classic_lock)
else else
piece:addGravity(math.max(gravity, drop_speed), grid) piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
end end
elseif inputs["up"] == true and hard_drop_enabled == true then elseif inputs["up"] == true and hard_drop_enabled == true then
if hard_drop_locked == true or piece:isDropBlocked(grid) then if hard_drop_locked == true or piece:isDropBlocked(grid) then
piece:addGravity(gravity, grid) piece:addGravity(gravity, grid, classic_lock)
else else
piece:dropToBottom(grid) piece:dropToBottom(grid)
end end
else else
piece:addGravity(gravity, grid) piece:addGravity(gravity, grid, classic_lock)
end end
if piece.position.y ~= y then if piece.position.y ~= y then
self:onPieceDrop(piece, grid) self:onPieceDrop(piece, grid)
end end
end end
function Ruleset:lockPiece(piece, grid, lock_delay) function Ruleset:lockPiece(piece, grid, lock_delay, classic_lock)
if piece:isDropBlocked(grid) and piece.gravity >= 1 and piece.lock_delay >= lock_delay then if piece:isDropBlocked(grid) and (
(classic_lock and piece.gravity >= 1) or
(not classic_lock and piece.lock_delay >= lock_delay)
) then
piece.locked = true piece.locked = true
end end
end end
function Ruleset:get180RotationValue() return 2 end function Ruleset:get180RotationValue() return 2 end
function Ruleset:getDefaultOrientation() return 1 end function Ruleset:getDefaultOrientation() return 1 end
function Ruleset:getDrawOffset(shape, orientation) return { x=0, y=0 } end
function Ruleset:getAboveFieldOffset(shape, orientation)
if shape == "I" then
return 1
else
return 2
end
end
function Ruleset:initializePiece( function Ruleset:initializePiece(
inputs, data, grid, gravity, prev_inputs, inputs, data, grid, gravity, prev_inputs,
@@ -230,26 +216,40 @@ function Ruleset:initializePiece(
else else
spawn_positions = self.spawn_positions spawn_positions = self.spawn_positions
end end
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
local colours
if self.pieces == 7 then
colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
else
colours = self.colourscheme
end
local spawn_x local spawn_x
if (grid.width ~= 10) then if (grid.width ~= 10) then
local percent = spawn_positions[data.shape].x / 10 local percent = spawn_positions[data.shape].x / 10
for i = 0, grid.width - 1 do for i = grid.width - 1, 0, -1 do
if i / grid.width >= percent then if i / grid.width <= percent then
spawn_x = i spawn_x = i
break break
end end
end end
end end
local spawn_dy = ( local spawn_dy
config.gamesettings.spawn_positions == 2 and if (config.gamesettings.spawn_positions == 1) then
2 or 0 spawn_dy = (
) self.spawn_above_field and
self:getAboveFieldOffset(data.shape, data.orientation) or 0
)
else
spawn_dy = (
config.gamesettings.spawn_positions == 3 and
self:getAboveFieldOffset(data.shape, data.orientation) or 0
)
end
local piece = Piece(data.shape, data.orientation - 1, { local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_x and spawn_x or spawn_positions[data.shape].x, x = spawn_x or spawn_positions[data.shape].x,
y = spawn_positions[data.shape].y - spawn_dy y = spawn_positions[data.shape].y - spawn_dy
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big) }, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
@@ -278,7 +278,7 @@ function Ruleset:processPiece(
inputs, piece, grid, gravity, prev_inputs, inputs, piece, grid, gravity, prev_inputs,
move, lock_delay, drop_speed, move, lock_delay, drop_speed,
drop_locked, hard_drop_locked, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity hard_drop_enabled, additive_gravity, classic_lock
) )
local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed] local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed]
@@ -292,11 +292,13 @@ function Ruleset:processPiece(
end end
self:dropPiece( self:dropPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked, inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity hard_drop_enabled, additive_gravity, classic_lock
) )
self:lockPiece(piece, grid, lock_delay) self:lockPiece(piece, grid, lock_delay, classic_lock)
end end
function Ruleset:canPieceMove(piece, grid) return true end
function Ruleset:canPieceRotate(piece, grid) return true end
function Ruleset:onPieceMove(piece) end function Ruleset:onPieceMove(piece) end
function Ruleset:onPieceRotate(piece) end function Ruleset:onPieceRotate(piece) end
function Ruleset:onPieceDrop(piece) end function Ruleset:onPieceDrop(piece) end

View File

@@ -92,11 +92,14 @@ function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:checkNewLow(piece) self:checkNewLow(piece)
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece:isDropBlocked(grid) then if piece.manipulations >= self.MANIPULATIONS_MAX then
if piece.manipulations >= SRS.MANIPULATIONS_MAX then piece:moveInGrid({ x = 0, y = 1 }, 1, grid)
piece.locked = true if piece:isDropBlocked(grid) then
end piece.locked = true
end end
end
end end
function SRS:canPieceRotate() return true end
return SRS return SRS

View File

@@ -54,7 +54,7 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid) self:onPieceRotate(piece, grid, offset.y < 0)
return return
end end
end end
@@ -69,7 +69,10 @@ end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
self:checkNewLow(piece) self:checkNewLow(piece)
if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then if (
piece.manipulations >= self.MANIPULATIONS_MAX or
piece.rotations >= self.ROTATIONS_MAX
) and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
else else
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
@@ -86,16 +89,18 @@ function SRS:onPieceMove(piece, grid)
end end
end end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid, upward)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:checkNewLow(piece) if upward or piece:isDropBlocked(grid) then
if piece.rotations >= self.ROTATIONS_MAX then piece.rotations = piece.rotations + 1
piece.rotations = piece.rotations + 1 if piece.rotations >= self.ROTATIONS_MAX and piece:isDropBlocked(grid) then
piece:moveInGrid({ x = 0, y = 1 }, 1, grid) piece.locked = true
if piece:isDropBlocked(grid) then end
piece.locked = true end
end end
end
function SRS:canPieceRotate(piece)
return piece.rotations < self.ROTATIONS_MAX
end end
function SRS:get180RotationValue() return 2 end function SRS:get180RotationValue() return 2 end

View File

@@ -150,9 +150,9 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
for idx, offset in pairs(kicks) do for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset) kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
piece:setOffset(offset) piece:setOffset(offset)
self:onPieceRotate(piece, grid, offset.y < 0)
return return
end end
end end
@@ -182,16 +182,20 @@ function SRS:onPieceMove(piece, grid)
end end
end end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid, upward)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then if upward or piece:isDropBlocked(grid) then
piece.rotations = piece.rotations + 1 piece.rotations = piece.rotations + 1
if piece.rotations >= self.ROTATIONS_MAX then if piece.rotations >= self.ROTATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
end end
end end
end end
function SRS:canPieceRotate(piece)
return piece.rotations < self.ROTATIONS_MAX
end
function SRS:get180RotationValue() return 3 end function SRS:get180RotationValue() return 3 end
return SRS return SRS