Compare commits

..

60 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
9f570306f5 Init 2021-01-26 13:36:50 -05:00
Ishaan Bhardwaj
b22f671409 2020, A2, A3 section time draw fixes 2021-01-25 22:26:55 -05:00
Ishaan Bhardwaj
0b6f62d50e Applied a fix for locking big pieces out of the grid 2021-01-25 16:34:22 -05:00
Ishaan Bhardwaj
086f327371 Large commit, read below
DAS Cut Delay added and configurable (like ARR and DAS)
BigInt lib added
IRS / IHS do not take effect when ARE = 0
Game now saves highscore correctly on game over
2021-01-24 14:55:35 -05:00
Ishaan Bhardwaj
3c83ae0bf4 Fixed stray ends 2021-01-23 13:50:40 -05:00
Ishaan Bhardwaj
450833b246 Instant ARR fix on grids not 10-wide 2021-01-23 11:35:07 -05:00
Ishaan Bhardwaj
8e7a5418dc Fixed how grade points decay in A2 and A3 2021-01-23 11:34:46 -05:00
Ishaan Bhardwaj
6609b642dc formatBigNum prettifier 2021-01-20 10:53:39 -05:00
Ishaan Bhardwaj
452879ebab Fixed Marathon A3 section times, read comments
Some modes may not launch currently, will fix
2021-01-20 10:53:22 -05:00
Ishaan Bhardwaj
70a827b477 fixed A2 point decay 2021-01-16 13:27:07 -05:00
Ishaan Bhardwaj
d281a732db fixed A2 M-roll reqs again 2021-01-16 12:57:20 -05:00
Ishaan Bhardwaj
01e91fbd93 Fixes issues with retrying modes with BGM 2021-01-16 09:34:41 -05:00
Ishaan Bhardwaj
ece853c9d3 Swapped opacity and brightness for hold color 2021-01-15 16:00:15 -05:00
Ishaan Bhardwaj
ea8d008370 Set piece opacity fixes 2021-01-15 15:46:28 -05:00
Ishaan Bhardwaj
e20eb048c8 Game over animation (customizable per mode) 2021-01-14 21:51:47 -05:00
Ishaan Bhardwaj
a33ca1af24 Fixed a bug where you could not get M-roll in A2 2021-01-14 19:34:02 -05:00
Ishaan Bhardwaj
664bca2282 Fixed a notorious ARR bug 2021-01-14 19:27:20 -05:00
Ishaan Bhardwaj
fc8fb8b66f Added immobile spin bonus toggle (read comments)
Use piece.spin in your onPieceLock method to check for a spin
2021-01-14 19:22:53 -05:00
Ishaan Bhardwaj
fc58e6e908 Square mode added as toggle
Other things may get toggles too
Immobile spins, cascade, credit roll?
2021-01-14 17:52:23 -05:00
Ishaan Bhardwaj
061f6f5164 Square mode update 2021-01-14 16:28:18 -05:00
Ishaan Bhardwaj
4e9cea7dda Another bottom SFX bug fix 2021-01-12 15:57:45 -05:00
Ishaan Bhardwaj
fa97216167 Minor piece bottom SFX fix 2021-01-12 15:20:22 -05:00
Ishaan Bhardwaj
3f8d68cc9d Small game / settings fix 2021-01-12 14:32:10 -05:00
Ishaan Bhardwaj
6639d73c1c Spawn positions are now configurable 2021-01-12 13:47:03 -05:00
Ishaan Bhardwaj
668f061077 Fixed drawing frame on non-standard grids 2021-01-11 22:40:48 -05:00
Ishaan Bhardwaj
cb70967b82 Default field graphic fix 2021-01-11 15:52:11 -05:00
Ishaan Bhardwaj
0c2ba5f0cc Custom field heights implemented 2021-01-11 15:46:43 -05:00
Ishaan Bhardwaj
6d07a3b820 Removed outdated functions 2021-01-11 15:27:18 -05:00
Ishaan Bhardwaj
2de13a97f0 10-wide graphic restored 2021-01-11 15:17:32 -05:00
Ishaan Bhardwaj
512c2149f0 Adjusted spawn x positions 2021-01-11 14:48:03 -05:00
Ishaan Bhardwaj
6fb19220b7 Marathon A1 is back to 10 wide 2021-01-10 23:00:03 -05:00
Ishaan Bhardwaj
08da67c434 Merge pull request #13 from SashLilac/arbitrary-widths
Init arbitrary widths functionality.
2021-01-10 22:59:05 -05:00
Joe Zeng
2d63ca8ee1 Changed row initialization to also use parametrized width. 2021-01-10 22:52:56 -05:00
Ishaan Bhardwaj
0f09d47e60 Init arbitrary widths 2021-01-10 22:40:13 -05:00
Ishaan Bhardwaj
9d44d1e771 Fixed big mode gravity being twice as big 2021-01-10 22:01:25 -05:00
Ishaan Bhardwaj
5d022f9037 Rulesets can offset next queue draws (read below)
A ruleset can now have offsets for where pieces should be drawn in queue
No rulesets use this *yet*
2021-01-10 16:42:48 -05:00
Ishaan Bhardwaj
818743fe77 No "RANDOM PIECES ACTIVE!" on Sakura for pentos 2021-01-10 16:31:48 -05:00
Ishaan Bhardwaj
f22424d671 Update README.md - loading custom assets 2021-01-10 13:39:28 -05:00
Ishaan Bhardwaj
dd6baf1fe6 Draw outline now has line clear anim 2021-01-10 11:41:34 -05:00
Ishaan Bhardwaj
11cf5a9d55 Spawn SE bugfix 2021-01-10 11:15:36 -05:00
Ishaan Bhardwaj
5642ed1326 Added a ruleset toggle for ARE. 2021-01-09 23:17:24 -05:00
Ishaan Bhardwaj
c0888c484f Fixed the first easter egg 2021-01-08 20:33:44 -05:00
Ishaan Bhardwaj
3ef3b193fd 3-tall pentoes spawn highest on 21 now 2021-01-08 17:16:15 -05:00
Ishaan Bhardwaj
0c2e3efd1a PAIRS anti-stall added 2021-01-08 16:46:19 -05:00
Ishaan Bhardwaj
5076adf022 Secret inputs fix 2021-01-08 13:59:42 -05:00
Ishaan Bhardwaj
1a75d983dc Corrected PAIRS big spawns 2021-01-07 20:53:36 -05:00
Ishaan Bhardwaj
5b8e9586bd Sakura bugfixes 2021-01-07 19:59:11 -05:00
Ishaan Bhardwaj
7d7dd8c3c2 Roll roll bugfixes 2021-01-07 19:52:36 -05:00
Ishaan Bhardwaj
29afdcecfc PAIRS I5 and U spawns fixed 2021-01-07 19:06:37 -05:00
Ishaan Bhardwaj
8b09833ae6 PAIRS added, with bugfixes 2021-01-07 18:42:49 -05:00
Ishaan Bhardwaj
64047eaf9c Slight randomizer logic change, PAIRS incoming 2021-01-07 16:53:46 -05:00
Ishaan Bhardwaj
125488b4d9 Can no longer buffer a hard drop when not allowed 2021-01-06 23:06:51 -05:00
Ishaan Bhardwaj
1fdd091456 Ruleset and randomizer refactoring (Read comments)
You can now specify an arbitrary number of pieces for a ruleset.
The randomizers will adjust accordingly.
Expect a pento ruleset in the modpack soon!
Also, gamemode skin selection has been refactored.
2021-01-06 22:53:44 -05:00
Ishaan Bhardwaj
ced40297cc Line clear anim part 3 2021-01-06 21:37:51 -05:00
Ishaan Bhardwaj
32f2a0b3e7 Line clear anim part 2 2021-01-06 18:01:56 -05:00
Ishaan Bhardwaj
dd5347ad8d (Beta) line clear animation 2021-01-06 16:56:44 -05:00
Ishaan Bhardwaj
b732ebb213 Credits scene no longer plays while not focused 2021-01-06 16:10:01 -05:00
Ishaan Bhardwaj
84634d6933 Added an option to control buffer locking.
You can now choose if you want a drop input
during ARE to lock the piece on the first frame it is active.
2021-01-06 16:06:17 -05:00
Ishaan Bhardwaj
0d13a9f236 Can send inputs from mode select to game
Warning: this may break some things
2021-01-05 21:59:50 -05:00
Ishaan Bhardwaj
45120bc9f7 Update README MacOS instructions 2021-01-05 08:59:22 -05:00
34 changed files with 4871 additions and 308 deletions

View File

@@ -1,10 +1,5 @@
![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png) ![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png)
Important notice
================
![Tetra Online Notice](https://pbs.twimg.com/media/Eo3CkIHW8AEoK_U?format=png&name=small)
Cambridge Cambridge
========= =========
@@ -65,6 +60,14 @@ Then, check the mod pack section at the bottom of this page.
If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!** If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!**
#### Downloading a release
You can download the .love file in the latest release, and run it with:
love cambridge.love
#### Installing from source
Clone the repository in git: Clone the repository in git:
git clone https://github.com/SashLilac/cambridge git clone https://github.com/SashLilac/cambridge
@@ -81,6 +84,8 @@ It should run automatically!
Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically. Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically.
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! **WARNING:** The .exe / .love files and the bleeding edge releases have different save directories. Read the above link carefully!
For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential. For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.

View File

@@ -140,3 +140,33 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.
bigint.lua (https://github.com/empyreuma/bigint.lua)
--------------------
3-Clause BSD License
Copyright (c) Emily "empyreuma" 2016
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -56,9 +56,19 @@ end
function formatBigNum(number) function formatBigNum(number)
-- returns a string representing a number with commas as thousands separator (e.g. 12,345,678) -- returns a string representing a number with commas as thousands separator (e.g. 12,345,678)
local s = string.format("%d", number) local s
local pos = string.len(s) % 3 if type(number) == "number" then
if pos == 0 then pos = 3 end s = string.format("%d", number)
elseif type(number) == "string" then
if not tonumber(number) then
return
else
s = number
end
else
return
end
local pos = Mod1(string.len(s), 3)
return string.sub(s, 1, pos) return string.sub(s, 1, pos)
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1") .. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
end end
@@ -67,3 +77,12 @@ function Mod1(n, m)
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1]) -- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
return ((n-1) % m) + 1 return ((n-1) % m) + 1
end end
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

558
libs/bigint/bigint.lua Normal file
View File

@@ -0,0 +1,558 @@
#!/usr/bin/env lua
-- If this variable is true, then strict type checking is performed for all
-- operations. This may result in slower code, but it will allow you to catch
-- errors and bugs earlier.
local strict = true
--------------------------------------------------------------------------------
local bigint = {}
local named_powers = require("libs.bigint.named-powers-of-ten")
-- Create a new bigint or convert a number or string into a big
-- Returns an empty, positive bigint if no number or string is given
function bigint.new(num)
local self = {
sign = "+",
digits = {}
}
-- Return a new bigint with the same sign and digits
function self:clone()
local newint = bigint.new()
newint.sign = self.sign
for _, digit in pairs(self.digits) do
newint.digits[#newint.digits + 1] = digit
end
return newint
end
setmetatable(self, {
__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,
__tostring = function()
return bigint.unserialize(self, "s")
end
})
if (num) then
local num_string = tostring(num)
for digit in string.gmatch(num_string, "[0-9]") do
table.insert(self.digits, tonumber(digit))
end
if string.sub(num_string, 1, 1) == "-" then
self.sign = "-"
end
end
return self
end
-- Check the type of a big
-- Normally only runs when global variable "strict" == true, but checking can be
-- forced by supplying "true" as the second argument.
function bigint.check(big, force)
if (strict or force) then
assert(#big.digits > 0, "bigint is empty")
assert(type(big.sign) == "string", "bigint is unsigned")
for _, digit in pairs(big.digits) do
assert(type(digit) == "number", digit .. " is not a number")
assert(digit < 10, digit .. " is greater than or equal to 10")
end
end
return true
end
-- Return a new big with the same digits but with a positive sign (absolute
-- value)
function bigint.abs(big)
bigint.check(big)
local result = big:clone()
result.sign = "+"
return result
end
-- Convert a big to a number or string
function bigint.unserialize(big, output_type, precision)
bigint.check(big)
local num = ""
if big.sign == "-" then
num = "-"
end
if ((output_type == nil)
or (output_type == "number")
or (output_type == "n")
or (output_type == "string")
or (output_type == "s")) then
-- Unserialization to a string or number requires reconstructing the
-- entire number
for _, digit in pairs(big.digits) do
num = num .. math.floor(digit) -- lazy way of getting rid of .0$
end
if ((output_type == nil)
or (output_type == "number")
or (output_type == "n")) then
return tonumber(num)
else
return num
end
else
-- Unserialization to human-readable form or scientific notation only
-- requires reading the first few digits
if (precision == nil) then
precision = 3
else
assert(precision > 0, "Precision cannot be less than 1")
assert(math.floor(precision) == precision,
"Precision must be a positive integer")
end
-- num is the first (precision + 1) digits, the first being separated by
-- a decimal point from the others
num = num .. big.digits[1]
if (precision > 1) then
num = num .. "."
for i = 1, (precision - 1) do
num = num .. big.digits[i + 1]
end
end
if ((output_type == "human-readable")
or (output_type == "human")
or (output_type == "h")) then
-- Human-readable output contributed by 123eee555
local name
local walkback = 0 -- Used to enumerate "ten", "hundred", etc
-- Walk backwards in the index of named_powers starting at the
-- number of digits of the input until the first value is found
for i = (#big.digits - 1), (#big.digits - 4), -1 do
name = named_powers[i]
if (name) then
if (walkback == 1) then
name = "ten " .. name
elseif (walkback == 2) then
name = "hundred " .. name
end
break
else
walkback = walkback + 1
end
end
return num .. " " .. name
else
return num .. "*10^" .. (#big.digits - 1)
end
end
end
-- Basic comparisons
-- Accepts symbols (<, >=, ~=) and Unix shell-like options (lt, ge, ne)
function bigint.compare(big1, big2, comparison)
bigint.check(big1)
bigint.check(big2)
local greater = false -- If big1.digits > big2.digits
local equal = false
if (big1.sign == "-") and (big2.sign == "+") then
greater = false
elseif (#big1.digits > #big2.digits)
or ((big1.sign == "+") and (big2.sign == "-")) then
greater = true
elseif (#big1.digits == #big2.digits) then
-- Walk left to right, comparing digits
for digit = 1, #big1.digits do
if (big1.digits[digit] > big2.digits[digit]) then
greater = true
break
elseif (big2.digits[digit] > big1.digits[digit]) then
break
elseif (digit == #big1.digits)
and (big1.digits[digit] == big2.digits[digit]) then
equal = true
end
end
end
-- If both numbers are negative, then the requirements for greater are
-- reversed
if (not equal) and (big1.sign == "-") and (big2.sign == "-") then
greater = not greater
end
return (((comparison == "<") or (comparison == "lt"))
and ((not greater) and (not equal)) and true)
or (((comparison == ">") or (comparison == "gt"))
and ((greater) and (not equal)) and true)
or (((comparison == "==") or (comparison == "eq"))
and (equal) and true)
or (((comparison == ">=") or (comparison == "ge"))
and (equal or greater) and true)
or (((comparison == "<=") or (comparison == "le"))
and (equal or not greater) and true)
or (((comparison == "~=") or (comparison == "!=") or (comparison == "ne"))
and (not equal) and true)
or false
end
-- BACKEND: Add big1 and big2, ignoring signs
function bigint.add_raw(big1, big2)
bigint.check(big1)
bigint.check(big2)
local result = bigint.new()
local max_digits = 0
local carry = 0
if (#big1.digits >= #big2.digits) then
max_digits = #big1.digits
else
max_digits = #big2.digits
end
-- Walk backwards right to left, like in long addition
for digit = 0, max_digits - 1 do
local sum = (big1.digits[#big1.digits - digit] or 0)
+ (big2.digits[#big2.digits - digit] or 0)
+ carry
if (sum >= 10) then
carry = 1
sum = sum - 10
else
carry = 0
end
result.digits[max_digits - digit] = sum
end
-- Leftover carry in cases when #big1.digits == #big2.digits and sum > 10, ex. 7 + 9
if (carry == 1) then
table.insert(result.digits, 1, 1)
end
return result
end
-- BACKEND: Subtract big2 from big1, ignoring signs
function bigint.subtract_raw(big1, big2)
-- Type checking is done by bigint.compare
assert(bigint.compare(bigint.abs(big1), bigint.abs(big2), ">="),
"Size of " .. bigint.unserialize(big1, "string") .. " is less than "
.. bigint.unserialize(big2, "string"))
local result = big1:clone()
local max_digits = #big1.digits
local borrow = 0
-- Logic mostly copied from bigint.add_raw ---------------------------------
-- Walk backwards right to left, like in long subtraction
for digit = 0, max_digits - 1 do
local diff = (big1.digits[#big1.digits - digit] or 0)
- (big2.digits[#big2.digits - digit] or 0)
- borrow
if (diff < 0) then
borrow = 1
diff = diff + 10
else
borrow = 0
end
result.digits[max_digits - digit] = diff
end
----------------------------------------------------------------------------
-- Strip leading zeroes if any, but not if 0 is the only digit
while (#result.digits > 1) and (result.digits[1] == 0) do
table.remove(result.digits, 1)
end
return result
end
-- FRONTEND: Addition and subtraction operations, accounting for signs
function bigint.add(big1, big2)
-- Type checking is done by bigint.compare
local result
-- If adding numbers of different sign, subtract the smaller sized one from
-- the bigger sized one and take the sign of the bigger sized one
if (big1.sign ~= big2.sign) then
if (bigint.compare(bigint.abs(big1), bigint.abs(big2), ">")) then
result = bigint.subtract_raw(big1, big2)
result.sign = big1.sign
else
result = bigint.subtract_raw(big2, big1)
result.sign = big2.sign
end
elseif (big1.sign == "+") and (big2.sign == "+") then
result = bigint.add_raw(big1, big2)
elseif (big1.sign == "-") and (big2.sign == "-") then
result = bigint.add_raw(big1, big2)
result.sign = "-"
end
return result
end
function bigint.subtract(big1, big2)
-- Type checking is done by bigint.compare in bigint.add
-- Subtracting is like adding a negative
local big2_local = big2:clone()
if (big2.sign == "+") then
big2_local.sign = "-"
else
big2_local.sign = "+"
end
return bigint.add(big1, big2_local)
end
-- BACKEND: Multiply a big by a single digit big, ignoring signs
function bigint.multiply_single(big1, big2)
bigint.check(big1)
bigint.check(big2)
assert(#big2.digits == 1, bigint.unserialize(big2, "string")
.. " has more than one digit")
local result = bigint.new()
local carry = 0
-- Logic mostly copied from bigint.add_raw ---------------------------------
-- Walk backwards right to left, like in long multiplication
for digit = 0, #big1.digits - 1 do
local this_digit = big1.digits[#big1.digits - digit]
* big2.digits[1]
+ carry
if (this_digit >= 10) then
carry = math.floor(this_digit / 10)
this_digit = this_digit - (carry * 10)
else
carry = 0
end
result.digits[#big1.digits - digit] = this_digit
end
-- Leftover carry in cases when big1.digits[1] * big2.digits[1] > 0
if (carry > 0) then
table.insert(result.digits, 1, carry)
end
----------------------------------------------------------------------------
return result
end
-- FRONTEND: Multiply two bigs, accounting for signs
function bigint.multiply(big1, big2)
-- Type checking done by bigint.multiply_single
local result = bigint.new(0)
local larger, smaller -- Larger and smaller in terms of digits, not size
if (bigint.unserialize(big1) == 0) or (bigint.unserialize(big2) == 0) then
return result
end
if (#big1.digits >= #big2.digits) then
larger = big1
smaller = big2
else
larger = big2
smaller = big1
end
-- Walk backwards right to left, like in long multiplication
for digit = 0, #smaller.digits - 1 do
-- Sorry for going over column 80! There's lots of big names here
local this_digit_product = bigint.multiply_single(larger,
bigint.new(smaller.digits[#smaller.digits - digit]))
-- "Placeholding zeroes"
if (digit > 0) then
for placeholder = 1, digit do
table.insert(this_digit_product.digits, 0)
end
end
result = bigint.add(result, this_digit_product)
end
if (larger.sign == smaller.sign) then
result.sign = "+"
else
result.sign = "-"
end
return result
end
-- Raise a big to a positive integer or big power (TODO: negative integer power)
function bigint.exponentiate(big, power)
-- Type checking for big done by bigint.multiply
assert(bigint.compare(power, bigint.new(0), ">="),
" negative powers are not supported")
local exp = power:clone()
if (bigint.compare(exp, bigint.new(0), "==")) then
return bigint.new(1)
elseif (bigint.compare(exp, bigint.new(1), "==")) then
return big
else
local result = big:clone()
while (bigint.compare(exp, bigint.new(1), ">")) do
result = bigint.multiply(result, big)
exp = bigint.subtract(exp, bigint.new(1))
end
return result
end
end
-- BACKEND: Divide two bigs (decimals not supported), returning big result and
-- big remainder
-- WARNING: Only supports positive integers
function bigint.divide_raw(big1, big2)
-- Type checking done by bigint.compare
if (bigint.compare(big1, big2, "==")) then
return bigint.new(1), bigint.new(0)
elseif (bigint.compare(big1, big2, "<")) then
return bigint.new(0), bigint.new(0)
else
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
assert(big1.sign == "+", "error: big1 is not positive")
assert(big2.sign == "+", "error: big2 is not positive")
local result = bigint.new()
local dividend = bigint.new() -- Dividend of a single operation, not the
-- dividend of the overall function
local divisor = big2:clone()
local factor = 1
-- Walk left to right among digits in the dividend, like in long
-- division
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
-- to the result and the loop ends
if (bigint.compare(dividend, divisor, "<")) then
if (#result.digits > 0) then -- Don't add leading zeroes
result.digits[#result.digits + 1] = 0
end
else
-- Find the maximum number of divisors that fit into the
-- dividend
factor = 0
while (bigint.compare(divisor, dividend, "<=")) do
divisor = bigint.add(divisor, big2)
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
-- The remainder of the final loop is returned as the function's
-- overall remainder
return result, dividend
end
end
-- FRONTEND: Divide two bigs (decimals not supported), returning big result and
-- big remainder, accounting for signs
function bigint.divide(big1, big2)
local result, remainder = bigint.divide_raw(bigint.abs(big1),
bigint.abs(big2))
if (big1.sign == big2.sign) then
result.sign = "+"
else
result.sign = "-"
end
return result, remainder
end
-- FRONTEND: Return only the remainder from bigint.divide
function bigint.modulus(big1, big2)
local result, remainder = bigint.divide(big1, big2)
-- Remainder will always have the same sign as the dividend per C standard
-- https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation
remainder.sign = big1.sign
return remainder
end
return bigint

File diff suppressed because it is too large Load Diff

2
load/bigint.lua Normal file
View File

@@ -0,0 +1,2 @@
bigint = require "libs.bigint.bigint"
number_names = require "libs.bigint.named-powers-of-ten"

View File

@@ -61,6 +61,11 @@ blocks = {
F = love.graphics.newImage("res/img/gem9.png"), F = love.graphics.newImage("res/img/gem9.png"),
A = love.graphics.newImage("res/img/gem9.png"), A = love.graphics.newImage("res/img/gem9.png"),
X = love.graphics.newImage("res/img/gem9.png"), X = love.graphics.newImage("res/img/gem9.png"),
},
["square"] = {
F = love.graphics.newImage("res/img/squares.png"),
Y = love.graphics.newImage("res/img/squareg.png"),
X = love.graphics.newImage("res/img/squares.png"),
} }
} }

View File

@@ -7,6 +7,7 @@ function love.load()
require "load.sounds" require "load.sounds"
require "load.bgm" require "load.bgm"
require "load.save" require "load.save"
require "load.bigint"
loadSave() loadSave()
require "scene" require "scene"
--config["side_next"] = false --config["side_next"] = false
@@ -15,8 +16,10 @@ function love.load()
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
-- init config
if not config.das then config.das = 10 end if not config.das then config.das = 10 end
if not config.arr then config.arr = 2 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.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end if not config.bgm_volume then config.bgm_volume = 0.5 end

BIN
res/img/squareg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

BIN
res/img/squares.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

View File

@@ -8,7 +8,9 @@ function CreditsScene:new()
end end
function CreditsScene:update() function CreditsScene:update()
self.frames = self.frames + 1 if love.window.hasFocus() then
self.frames = self.frames + 1
end
if self.frames >= 4200 then if self.frames >= 4200 then
playSE("mode_decide") playSE("mode_decide")
scene = TitleScene() scene = TitleScene()

View File

@@ -4,12 +4,13 @@ GameScene.title = "Game"
require 'load.save' require 'load.save'
function GameScene:new(game_mode, ruleset) 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.game = game_mode() self.secret_inputs = copy(inputs)
self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset() self.ruleset = ruleset()
self.game:initialize(self.ruleset) self.game:initialize(self.ruleset, self.secret_inputs)
self.inputs = { self.inputs = {
left=false, left=false,
right=false, right=false,
@@ -49,9 +50,37 @@ function GameScene:render()
) )
-- game frame -- game frame
love.graphics.draw(misc_graphics["frame"], 48, 64) if self.game.grid.width == 10 and self.game.grid.height == 24 then
love.graphics.draw(misc_graphics["frame"], 48, 64)
end
love.graphics.setColor(0, 0, 0, 200) love.graphics.setColor(0, 0, 0, 200)
love.graphics.rectangle("fill", 64, 80, 160, 320) love.graphics.rectangle(
"fill", 64, 80,
16 * self.game.grid.width, 16 * (self.game.grid.height - 4)
)
if self.game.grid.width ~= 10 or self.game.grid.height ~= 24 then
love.graphics.setColor(174/255, 83/255, 76/255, 1)
love.graphics.setLineWidth(8)
love.graphics.line(
60,76,
68+16*self.game.grid.width,76,
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
60,84+16*(self.game.grid.height-4),
60,76
)
love.graphics.setColor(203/255, 137/255, 111/255, 1)
love.graphics.setLineWidth(4)
love.graphics.line(
60,76,
68+16*self.game.grid.width,76,
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
60,84+16*(self.game.grid.height-4),
60,76
)
love.graphics.setLineWidth(1)
end
self.game:drawGrid() self.game:drawGrid()
self.game:drawPiece() self.game:drawPiece()
@@ -75,16 +104,23 @@ function GameScene:render()
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
if self.paused then love.graphics.print("PAUSED!", 80, 100) end if self.paused then love.graphics.print("PAUSED!", 80, 100) end
if self.game.completed then
self.game:onGameComplete()
elseif self.game.game_over then
self.game:onGameOver()
end
end end
function GameScene:onInputPress(e) function GameScene:onInputPress(e)
if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then if (self.game.game_over or self.game.completed) and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
highscore_entry = self.game:getHighscoreData() highscore_entry = self.game:getHighscoreData()
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
submitHighscore(highscore_hash, highscore_entry) submitHighscore(highscore_hash, highscore_entry)
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) or ModeSelectScene() 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
scene = GameScene(self.retry_mode, self.retry_ruleset) switchBGM(nil)
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()
@@ -103,8 +139,19 @@ function GameScene:onInputRelease(e)
end end
function submitHighscore(hash, data) function submitHighscore(hash, data)
function isHighscore(score, high)
for k, _ in pairs(score) do
if not high[k] or score[k] > high[k] then
return true
end
end
return false
end
if not highscores[hash] then highscores[hash] = {} end if not highscores[hash] then highscores[hash] = {} end
table.insert(highscores[hash], data) if isHighscore(data, highscores[hash]) then
highscores[hash] = data
end
saveHighscores() saveHighscores()
end end

View File

@@ -11,11 +11,13 @@ 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"}},
{"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 Switch", false, {"Default", "Instant"}},
{"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"}},
{"buffer_lock", "Buffer Lock Inputs", false, {"On", "Off"}},
{"sfx_volume", "SFX", true, "sfxSlider"}, {"sfx_volume", "SFX", true, "sfxSlider"},
{"bgm_volume", "BGM", true, "bgmSlider"}, {"bgm_volume", "BGM", true, "bgmSlider"},
} }
@@ -31,8 +33,8 @@ function ConfigScene:new()
state = "Changing game settings", state = "Changing game settings",
}) })
self.sfxSlider = newSlider(165, 375, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20}) self.sfxSlider = newSlider(165, 400, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
self.bgmSlider = newSlider(465, 375, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20}) self.bgmSlider = newSlider(465, 400, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
end end
function ConfigScene:update() function ConfigScene:update()
@@ -55,9 +57,9 @@ function ConfigScene:render()
--Lazy check to see if we're on the SFX or BGM slider. Probably will need to be rewritten if more options get added. --Lazy check to see if we're on the SFX or BGM slider. Probably will need to be rewritten if more options get added.
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
if not ConfigScene.options[self.highlight][3] then if not ConfigScene.options[self.highlight][3] then
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22) love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
else else
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 322, 215, 33) love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 342, 215, 33)
end end
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
@@ -74,8 +76,8 @@ function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 325) love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 345)
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 325) love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 345)
love.graphics.setColor(1, 1, 1, 0.75) love.graphics.setColor(1, 1, 1, 0.75)
self.sfxSlider:draw() self.sfxSlider:draw()

View File

@@ -11,6 +11,14 @@ function ModeSelectScene:new()
ruleset = current_ruleset, ruleset = current_ruleset,
select = "mode", select = "mode",
} }
self.secret_inputs = {
rotate_left = false,
rotate_left2 = false,
rotate_right = false,
rotate_right2 = false,
rotate_180 = false,
hold = false,
}
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = "Choosing a mode", state = "Choosing a mode",
@@ -33,14 +41,14 @@ function ModeSelectScene:render()
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
end end
love.graphics.rectangle("fill", 20, 258, 240, 22) love.graphics.rectangle("fill", 20, 198, 240, 22)
if self.menu_state.select == "mode" then if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
end end
love.graphics.rectangle("fill", 340, 258, 200, 22) love.graphics.rectangle("fill", 340, 198, 200, 22)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@@ -48,13 +56,36 @@ function ModeSelectScene:render()
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do for idx, mode in pairs(game_modes) do
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then if(idx >= self.menu_state.mode-6 and idx <= self.menu_state.mode+6) then
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left") love.graphics.printf(mode.name, 40, (200 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
end end
end end
for idx, ruleset in pairs(rulesets) do for idx, ruleset in pairs(rulesets) do
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then if(idx >= self.menu_state.ruleset-6 and idx <= self.menu_state.ruleset+6) then
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left") love.graphics.printf(ruleset.name, 360, (200 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
end
end
-- mode description and highscore
for midx, mode in pairs(game_modes) do
for ridx, ruleset in pairs(rulesets) do
if (midx == self.menu_state.mode) and (ridx == self.menu_state.ruleset) then
love.graphics.printf(
"Mode Description:\n\n" .. mode.tagline, 20, 350, 200, "left"
)
love.graphics.printf(
ruleset.name .. " Highscore:", 240, 350, 200, "right"
)
local highscore_string = ""
if highscores[mode.hash .. "-" .. ruleset.hash] then
for k, v in ipairs(highscores[mode.hash .. "-" .. ruleset.hash]) do
highscore_string = highscore_string .. k .. ": " .. v .. "\n"
end
else
highscore_string = "You don't have any highscores yet!"
end
love.graphics.printf(highscore_string, 450, 350, 200, "left")
end
end end
end end
end end
@@ -67,7 +98,7 @@ function ModeSelectScene:onInputPress(e)
config.current_ruleset = current_ruleset config.current_ruleset = current_ruleset
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset]) scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs)
elseif e.input == "up" or e.scancode == "up" then elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1) self:changeOption(-1)
playSE("cursor") playSE("cursor")
@@ -79,6 +110,14 @@ function ModeSelectScene:onInputPress(e)
playSE("cursor_lr") playSE("cursor_lr")
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
scene = TitleScene() scene = TitleScene()
elseif e.input then
self.secret_inputs[e.input] = true
end
end
function ModeSelectScene:onInputRelease(e)
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then
self.secret_inputs[e.input] = false
end end
end end

View File

@@ -72,6 +72,7 @@ function TitleScene:render()
) )
love.graphics.print("Happy Holidays!", 320, -100 + self.y_offset) love.graphics.print("Happy Holidays!", 320, -100 + self.y_offset)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print(self.restart_message and "Restart Cambridge..." or "", 0, 0) love.graphics.print(self.restart_message and "Restart Cambridge..." or "", 0, 0)
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)

View File

@@ -9,6 +9,7 @@ TuningScene.options = {
-- Serves as a reference for the options available in the menu. Format: {name in config, name as displayed if applicable, slider name} -- Serves as a reference for the options available in the menu. Format: {name in config, name as displayed if applicable, slider name}
{"das", "DAS", "dasSlider"}, {"das", "DAS", "dasSlider"},
{"arr", "ARR", "arrSlider"}, {"arr", "ARR", "arrSlider"},
{"dcd", "DCD", "dcdSlider"},
} }
local optioncount = #TuningScene.options local optioncount = #TuningScene.options
@@ -20,13 +21,15 @@ function TuningScene:new()
}) })
self.highlight = 1 self.highlight = 1
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20}) self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
self.arrSlider = newSlider(290, 325, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20}) self.arrSlider = newSlider(290, 300, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
self.dcdSlider = newSlider(290, 375, 400, config.dcd, 0, 6, function(v) config.dcd = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
end end
function TuningScene:update() function TuningScene:update()
self.dasSlider:update() self.dasSlider:update()
self.arrSlider:update() self.arrSlider:update()
self.dcdSlider:update()
end end
function TuningScene:render() function TuningScene:render()
@@ -38,7 +41,7 @@ function TuningScene:render()
) )
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 75, 73 + self.highlight * 100, 400, 33) love.graphics.rectangle("fill", 75, 98 + self.highlight * 75, 400, 33)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@@ -50,11 +53,13 @@ function TuningScene:render()
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.print("Delayed Auto-Shift (DAS): " .. math.floor(self.dasSlider:getValue()) .. "F", 80, 175) love.graphics.print("Delayed Auto-Shift (DAS): " .. math.floor(self.dasSlider:getValue()) .. "F", 80, 175)
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 275) love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 250)
love.graphics.print("DAS Cut Delay (DCD): " .. math.floor(self.dcdSlider:getValue()) .. "F", 80, 325)
love.graphics.setColor(1, 1, 1, 0.75) love.graphics.setColor(1, 1, 1, 0.75)
self.dasSlider:draw() self.dasSlider:draw()
self.arrSlider:draw() self.arrSlider:draw()
self.dcdSlider:draw()
end end
function TuningScene:onInputPress(e) function TuningScene:onInputPress(e)

View File

@@ -6,13 +6,15 @@ local empty = { skin = "", colour = "" }
local oob = { skin = "", colour = "" } local oob = { skin = "", colour = "" }
local block = { skin = "2tie", colour = "A" } local block = { skin = "2tie", colour = "A" }
function Grid:new() function Grid:new(width, height)
self.grid = {} self.grid = {}
self.grid_age = {} self.grid_age = {}
for y = 1, 24 do self.width = width
self.height = height
for y = 1, self.height do
self.grid[y] = {} self.grid[y] = {}
self.grid_age[y] = {} self.grid_age[y] = {}
for x = 1, 10 do for x = 1, self.width do
self.grid[y][x] = empty self.grid[y][x] = empty
self.grid_age[y][x] = 0 self.grid_age[y][x] = 0
end end
@@ -20,8 +22,8 @@ function Grid:new()
end end
function Grid:clear() function Grid:clear()
for y = 1, 24 do for y = 1, self.height do
for x = 1, 10 do for x = 1, self.width do
self.grid[y][x] = empty self.grid[y][x] = empty
self.grid_age[y][x] = 0 self.grid_age[y][x] = 0
end end
@@ -29,7 +31,7 @@ function Grid:clear()
end end
function Grid:getCell(x, y) function Grid:getCell(x, y)
if x < 1 or x > 10 or y > 24 then return oob if x < 1 or x > self.width or y > self.height then return oob
elseif y < 1 then return empty elseif y < 1 then return empty
else return self.grid[y][x] else return self.grid[y][x]
end end
@@ -98,22 +100,25 @@ end
function Grid:getClearedRowCount() function Grid:getClearedRowCount()
local count = 0 local count = 0
for row = 1, 24 do local cleared_row_table = {}
for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
count = count + 1 count = count + 1
table.insert(cleared_row_table, row)
end end
end end
return count return count, cleared_row_table
end end
function Grid:markClearedRows() function Grid:markClearedRows()
for row = 1, 24 do for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
for x = 1, 10 do for x = 1, self.width do
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
end end
end end
end end
@@ -121,66 +126,52 @@ function Grid:markClearedRows()
end end
function Grid:clearClearedRows() function Grid:clearClearedRows()
for row = 1, 24 do for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
for above_row = row, 2, -1 do for above_row = row, 2, -1 do
self.grid[above_row] = self.grid[above_row - 1] self.grid[above_row] = self.grid[above_row - 1]
self.grid_age[above_row] = self.grid_age[above_row - 1] self.grid_age[above_row] = self.grid_age[above_row - 1]
end end
self.grid[1] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty} self.grid[1] = {}
self.grid_age[1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} self.grid_age[1] = {}
for i = 1, self.width do
self.grid[1][i] = empty
self.grid_age[1][i] = 0
end
end end
end end
return true return true
end end
function Grid:copyBottomRow() function Grid:copyBottomRow()
for row = 1, 23 do for row = 1, self.height - 1 do
self.grid[row] = self.grid[row+1] self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1] self.grid_age[row] = self.grid_age[row+1]
end end
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty} self.grid[self.height] = {}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} self.grid_age[self.height] = {}
for col = 1, 10 do for i = 1, self.width do
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block self.grid[self.height][i] = (self.grid[self.height - 1][i] == empty) and empty or block
self.grid_age[self.height][i] = 0
end end
return true return true
end end
function Grid:garbageRise(row_vals) function Grid:garbageRise(row_vals)
for row = 1, 23 do for row = 1, self.height - 1 do
self.grid[row] = self.grid[row+1] self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1] self.grid_age[row] = self.grid_age[row+1]
end
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for col = 1, 10 do
self.grid[24][col] = (row_vals[col] == "e") and empty or block
end end
end self.grid[self.height] = {}
self.grid_age[self.height] = {}
function Grid:applyFourWide() for i = 1, self.width do
for row = 1, 24 do self.grid[self.height][i] = (row_vals[i] == "e") and empty or block
local x = self.grid[row] self.grid_age[self.height][i] = 0
x[1] = x[1]~=block and block or x[1]
x[2] = x[2]~=block and block or x[2]
x[3] = x[3]~=block and block or x[3]
x[8] = x[8]~=block and block or x[8]
x[9] = x[9]~=block and block or x[9]
x[10] = x[10]~=block and block or x[10]
end
end
function Grid:applyCeiling(lines)
for row = 1, lines do
for col = 1, 9 do
self.grid[row][col] = block
end
end end
end end
function Grid:clearSpecificRow(row) function Grid:clearSpecificRow(row)
for col = 1, 10 do for col = 1, self.width do
self.grid[row][col] = empty self.grid[row][col] = empty
end end
end end
@@ -194,7 +185,7 @@ function Grid:applyPiece(piece)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
x = piece.position.x + offset.x x = piece.position.x + offset.x
y = piece.position.y + offset.y y = piece.position.y + offset.y
if y + 1 > 0 and y < 24 then if y + 1 > 0 and y < self.height then
self.grid[y+1][x+1] = { self.grid[y+1][x+1] = {
skin = piece.skin, skin = piece.skin,
colour = piece.colour colour = piece.colour
@@ -210,7 +201,7 @@ function Grid:applyBigPiece(piece)
y = piece.position.y + offset.y y = piece.position.y + offset.y
for a = 1, 2 do for a = 1, 2 do
for b = 1, 2 do for b = 1, 2 do
if y*2+a > 0 then if y*2+a > 0 and y*2 < self.height then
self.grid[y*2+a][x*2+b] = { self.grid[y*2+a][x*2+b] = {
skin = piece.skin, skin = piece.skin,
colour = piece.colour colour = piece.colour
@@ -222,8 +213,8 @@ function Grid:applyBigPiece(piece)
end end
function Grid:checkForBravo(cleared_row_count) function Grid:checkForBravo(cleared_row_count)
for i = 0, 23 - cleared_row_count do for i = 0, self.height - 1 - cleared_row_count do
for j = 0, 9 do for j = 0, self.width - 1 do
if self:isOccupied(j, i) then return false end if self:isOccupied(j, i) then return false end
end end
end end
@@ -231,9 +222,9 @@ function Grid:checkForBravo(cleared_row_count)
end end
function Grid:checkStackHeight() function Grid:checkStackHeight()
for i = 0, 23 do for i = 0, self.height - 1 do
for j = 0, 9 do for j = 0, self.width - 1 do
if self:isOccupied(j, i) then return 24 - i end if self:isOccupied(j, i) then return self.height - i end
end end
end end
return 0 return 0
@@ -274,8 +265,8 @@ function Grid:checkSecretGrade()
end end
function Grid:hasGemBlocks() function Grid:hasGemBlocks()
for y = 1, 24 do for y = 1, self.height do
for x = 1, 10 do for x = 1, self.width do
if self.grid[y][x].skin == "gem" then if self.grid[y][x].skin == "gem" then
return true return true
end end
@@ -286,16 +277,16 @@ end
function Grid:mirror() function Grid:mirror()
local new_grid = {} local new_grid = {}
for y = 1, 24 do for y = 1, self.height do
new_grid[y] = {} new_grid[y] = {}
for x = 1, 10 do for x = 1, self.width do
new_grid[y][x] = empty new_grid[y][x] = empty
end end
end end
for y = 1, 24 do for y = 1, self.height do
for x = 1, 10 do for x = 1, self.width do
new_grid[y][x] = self.grid[y][11 - x] new_grid[y][x] = self.grid[y][self.width + 1 - x]
end end
end end
self.grid = new_grid self.grid = new_grid
@@ -310,9 +301,83 @@ function Grid:applyMap(map)
end end
end end
-- inefficient algorithm for squares
function Grid:markSquares()
-- goes up by 1 for silver, 2 for gold
local square_count = 0
for i = 1, 2 do
for y = 5, self.height - 3 do
for x = 1, self.width - 3 do
local age_table = {}
local age_count = 0
local colour_table = {}
local is_square = true
for j = 0, 3 do
for k = 0, 3 do
if self.grid[y+j][x+k].skin == "" or self.grid[y+j][x+k].skin == "square" then
is_square = false
end
if age_table[self.grid_age[y+j][x+k]] == nil then
age_table[self.grid_age[y+j][x+k]] = 1
age_count = age_count + 1
else
age_table[self.grid_age[y+j][x+k]] = age_table[self.grid_age[y+j][x+k]] + 1
end
if age_count > 4 or age_table[self.grid_age[y+j][x+k]] > 4 then
is_square = false
end
if not table.contains(colour_table, self.grid[y+j][x+k].colour) then
table.insert(colour_table, self.grid[y+j][x+k].colour)
end
end
end
if is_square then
if i == 1 and #colour_table == 1 then
for j = 0, 3 do
for k = 0, 3 do
self.grid[y+j][x+k].colour = "Y"
self.grid[y+j][x+k].skin = "square"
end
end
square_count = square_count + 2
elseif i == 2 then
for j = 0, 3 do
for k = 0, 3 do
self.grid[y+j][x+k].colour = "F"
self.grid[y+j][x+k].skin = "square"
end
end
square_count = square_count + 1
end
end
end
end
end
return square_count
end
-- square scan
function Grid:scanForSquares()
local table = {}
for row = 1, self.height do
local silver = 0
local gold = 0
for col = 1, self.width do
local colour = self.grid[row][col].colour
if self.grid[row][col].skin == "square" then
if colour == "Y" then gold = gold + 1
else silver = silver + 1 end
end
end
table[row] = gold * 2.5 + silver * 1.25
end
return table
end
function Grid:update() function Grid:update()
for y = 1, 24 do for y = 1, self.height do
for x = 1, 10 do for x = 1, self.width do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
self.grid_age[y][x] = self.grid_age[y][x] + 1 self.grid_age[y][x] = self.grid_age[y][x] + 1
end end
@@ -321,8 +386,8 @@ function Grid:update()
end end
function Grid:draw() function Grid:draw()
for y = 5, 24 do for y = 5, self.height do
for x = 1, 10 do for x = 1, self.width do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty 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)
@@ -330,24 +395,27 @@ function Grid:draw()
else else
if self.grid[y][x].skin == "bone" then if 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
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
end end
if self.grid[y][x].skin ~= "bone" then if self.grid[y][x].skin ~= "bone" 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)
if y > 1 and self.grid[y-1][x] == empty then if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < 24 and self.grid[y+1][x] == empty then if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16) love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end end
if x < 10 and self.grid[y][x+1] == empty then if x < self.width and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16) love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end end
end end
@@ -357,21 +425,26 @@ function Grid:draw()
end end
function Grid:drawOutline() function Grid:drawOutline()
for y = 5, 24 do for y = 5, self.height do
for x = 1, 10 do for x = 1, self.width do
if self.grid[y][x] ~= empty then 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
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)
if y > 1 and self.grid[y-1][x] == empty then if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < 24 and self.grid[y+1][x] == empty then if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16) love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end end
if x < 10 and self.grid[y][x+1] == empty then if x < self.width and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16) love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end end
end end
@@ -382,11 +455,11 @@ end
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness) function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
lock_flash = lock_flash == nil and true or lock_flash lock_flash = lock_flash == nil and true or lock_flash
brightness = brightness == nil and 0.5 or brightness brightness = brightness == nil and 0.5 or brightness
for y = 5, 24 do for y = 5, self.height do
for x = 1, 10 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 opacity = 1 - self.grid_age[y][x] / 15
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
@@ -398,16 +471,17 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
if opacity > 0 and self.grid[y][x].colour ~= "X" then if opacity > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64) love.graphics.setColor(0.64, 0.64, 0.64)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty then if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < 24 and self.grid[y+1][x] == empty then if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16) love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end end
if x < 10 and self.grid[y][x+1] == empty then if x < self.width and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16) love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end end
end end
@@ -426,31 +500,32 @@ function Grid:drawCustom(colour_function, gamestate)
gamestate: the gamemode instance itself to pass in colour_function gamestate: the gamemode instance itself to pass in colour_function
]] ]]
for y = 5, 24 do for y = 5, self.height do
for x = 1, 10 do for x = 1, self.width do
local block = self.grid[y][x] local block = self.grid[y][x]
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 A = 1 - self.grid_age[y][x] / 15
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)
if outline > 0 and self.grid[y][x].colour ~= "X" then if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline) love.graphics.setColor(0.64, 0.64, 0.64, outline)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty then if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < 24 and self.grid[y+1][x] == empty then if y < self.height and self.grid[y+1][x] == empty or
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) (y + 1 > self.height or self.grid[y+1][x].colour == "X") then
end love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
if x > 1 and self.grid[y][x-1] == empty then end
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16) if x > 1 and self.grid[y][x-1] == empty then
end love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
if x < 10 and self.grid[y][x+1] == empty then end
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16) if x < self.width and self.grid[y][x+1] == empty then
end love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end
end end
end end
end end

View File

@@ -5,14 +5,15 @@ local playedReadySE = false
local playedGoSE = false local playedGoSE = false
local Grid = require 'tetris.components.grid' local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer' local Randomizer = require 'tetris.randomizers.bag7'
local BagRandomizer = require 'tetris.randomizers.bag'
local GameMode = Object:extend() local GameMode = Object:extend()
GameMode.rollOpacityFunction = function(age) return 0 end GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new() function GameMode:new(secret_inputs)
self.grid = Grid() self.grid = Grid(10, 24)
self.randomizer = Randomizer() self.randomizer = Randomizer()
self.piece = nil self.piece = nil
self.ready_frames = 100 self.ready_frames = 100
@@ -21,6 +22,7 @@ function GameMode:new()
self.score = 0 self.score = 0
self.level = 0 self.level = 0
self.lines = 0 self.lines = 0
self.squares = 0
self.drop_bonus = 0 self.drop_bonus = 0
self.are = 0 self.are = 0
self.lcd = 0 self.lcd = 0
@@ -45,6 +47,8 @@ function GameMode:new()
self.big_mode = false self.big_mode = false
self.irs = true self.irs = true
self.ihs = true self.ihs = true
self.square_mode = false
self.immobile_spin_bonus = false
self.rpc_details = "In game" self.rpc_details = "In game"
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
@@ -56,6 +60,7 @@ function GameMode:new()
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.used_randomizer = nil
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
self.section_start_time = 0 self.section_start_time = 0
@@ -70,19 +75,32 @@ function GameMode:getLineARE() return 25 end
function GameMode:getLockDelay() return 30 end function GameMode:getLockDelay() return 30 end
function GameMode:getLineClearDelay() return 40 end function GameMode:getLineClearDelay() return 40 end
function GameMode:getDasLimit() return 15 end function GameMode:getDasLimit() return 15 end
function GameMode:getDasCutDelay() return 0 end
function GameMode:getNextPiece(ruleset) function GameMode:getNextPiece(ruleset)
return { return {
skin = "2tie", skin = self:getSkin(),
shape = self.randomizer:nextPiece(), shape = self.used_randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(), orientation = ruleset:getDefaultOrientation(),
} }
end end
function GameMode:initialize(ruleset) function GameMode:getSkin()
return "2tie"
end
function GameMode:initialize(ruleset, secret_inputs)
-- generate next queue -- generate next queue
self:new() self:new(secret_inputs)
self.used_randomizer = (
ruleset.pieces == self.randomizer.possible_pieces and
self.randomizer or
(
ruleset.pieces == 7 and
Randomizer() or
BagRandomizer(ruleset.pieces)
)
)
for i = 1, self.next_queue_length do for i = 1, self.next_queue_length do
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
@@ -91,14 +109,10 @@ function GameMode:initialize(ruleset)
end end
function GameMode:update(inputs, ruleset) function GameMode:update(inputs, ruleset)
if self.game_over then if self.game_over or self.completed then
self.game_over_frames = self.game_over_frames + 1 self.game_over_frames = self.game_over_frames + 1
if self.game_over_frames >= 60 then
self.completed = true
end
return return
end end
if self.completed then return end
if config.gamesettings.diagonal_input == 2 then if config.gamesettings.diagonal_input == 2 then
if inputs["left"] or inputs["right"] then if inputs["left"] or inputs["right"] then
@@ -113,16 +127,34 @@ function GameMode:update(inputs, ruleset)
-- advance one frame -- advance one frame
if self:advanceOneFrame(inputs, ruleset) == false then return end if self:advanceOneFrame(inputs, ruleset) == false then return end
self:chargeDAS(inputs, self:getDasLimit(), self.getARR()) self:chargeDAS(inputs, self:getDasLimit(), self:getARR())
-- set attempt flags -- set attempt flags
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end if inputs["left"] or inputs["right"] then
self:onAttemptPieceMove(self.piece)
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 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)
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
@@ -146,7 +178,9 @@ function GameMode:update(inputs, ruleset)
self.hard_drop_locked = false self.hard_drop_locked = false
end end
-- diff vars to use in checks
local piece_y = self.piece.position.y local piece_y = self.piece.position.y
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,
@@ -156,6 +190,22 @@ function GameMode:update(inputs, ruleset)
) )
local piece_dy = self.piece.position.y - piece_y local piece_dy = self.piece.position.y - piece_y
local piece_drot = self.piece.rotation - piece_rot
-- das cut
if (
(piece_dy ~= 0 and (inputs.up or inputs.down)) or
(piece_drot ~= 0 and (
inputs.rotate_left or inputs.rotate_right or
inputs.rotate_left2 or inputs.rotate_right2 or
inputs.rotate_180
))
) then
self.das.frames = math.max(
self.das.frames - self:getDasCutDelay(),
-self:getDasCutDelay()
)
end
if inputs["up"] == true and if inputs["up"] == true and
self.piece:isDropBlocked(self.grid) and self.piece:isDropBlocked(self.grid) and
@@ -179,13 +229,15 @@ function GameMode:update(inputs, ruleset)
if self.piece.locked == true then if self.piece.locked == true then
self.grid:applyPiece(self.piece) self.grid:applyPiece(self.piece)
self.grid:markClearedRows() if self.square_mode then
self.squares = self.squares + self.grid:markSquares()
end
local cleared_row_count = self.grid:getClearedRowCount() local cleared_row_count = self.grid:getClearedRowCount()
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.piece = nil self.piece = nil
if self.enable_hold then if self.enable_hold then
self.held = false self.held = false
@@ -194,7 +246,9 @@ 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.are = self:getLineARE() self.are = (
ruleset.are and self:getLineARE() or 0
)
if self.lcd == 0 then if self.lcd == 0 then
self.grid:clearClearedRows() self.grid:clearClearedRows()
if self.are == 0 then if self.are == 0 then
@@ -203,7 +257,7 @@ function GameMode:update(inputs, ruleset)
end end
self:onLineClear(cleared_row_count) self:onLineClear(cleared_row_count)
else else
if self:getARE() == 0 then if self:getARE() == 0 or not ruleset.are then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
else else
self.are = self:getARE() self.are = self:getARE()
@@ -247,6 +301,15 @@ end
function GameMode:onGameOver() function GameMode:onGameOver()
switchBGM(nil) switchBGM(nil)
love.graphics.setColor(0, 0, 0, 1 - 2 ^ (-self.game_over_frames / 30))
love.graphics.rectangle(
"fill", 64, 80,
16 * self.grid.width, 16 * (self.grid.height - 4)
)
end
function GameMode:onGameComplete()
self:onGameOver()
end end
-- DAS functions -- DAS functions
@@ -327,6 +390,12 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
playedGoSE = false playedGoSE = false
end end
if self.ready_frames > 0 then if self.ready_frames > 0 then
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
self.buffer_hard_drop = true
end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
if not playedReadySE then if not playedReadySE then
playedReadySE = true playedReadySE = true
playSEOnce("ready") playSEOnce("ready")
@@ -340,6 +409,12 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
elseif self.lcd > 0 then elseif self.lcd > 0 then
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
self.buffer_hard_drop = true
end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
self.lcd = self.lcd - 1 self.lcd = self.lcd - 1
self:areCancel(inputs, ruleset) self:areCancel(inputs, ruleset)
if self.lcd == 0 then if self.lcd == 0 then
@@ -350,6 +425,12 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end end
end end
elseif self.are > 0 then elseif self.are > 0 then
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
self.buffer_hard_drop = true
end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
self.are = self.are - 1 self.are = self.are - 1
self:areCancel(inputs, ruleset) self:areCancel(inputs, ruleset)
if self.are == 0 then if self.are == 0 then
@@ -359,14 +440,15 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end end
function GameMode:initializeOrHold(inputs, ruleset) function GameMode:initializeOrHold(inputs, ruleset)
if self.ihs and self.enable_hold and inputs["hold"] == true then if (
self.frames == 0 or (ruleset.are and self:getARE() ~= 0) and self.ihs or false
) and self.enable_hold and inputs["hold"] == true then
self:hold(inputs, ruleset, true) self:hold(inputs, ruleset, true)
else else
self:initializeNextPiece(inputs, ruleset, self.next_queue[1]) self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
end end
self:onPieceEnter() self:onPieceEnter()
if not self.grid:canPlacePiece(self.piece) then if not self.grid:canPlacePiece(self.piece) then
self:onGameOver()
self.game_over = true self.game_over = true
end end
end end
@@ -403,8 +485,27 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
self.prev_inputs, self.move, self.prev_inputs, self.move,
self:getLockDelay(), self:getDropSpeed(), self:getLockDelay(), self:getDropSpeed(),
self.lock_drop, self.lock_hard_drop, self.big_mode, self.lock_drop, self.lock_hard_drop, self.big_mode,
self.irs (
self.frames == 0 or (ruleset.are and self:getARE() ~= 0)
) and self.irs or false,
self.buffer_hard_drop, self.buffer_soft_drop,
self.lock_on_hard_drop, self.lock_on_soft_drop
) )
if self.piece:isDropBlocked(self.grid) and
self.grid:canPlacePiece(self.piece) then
playSE("bottom")
end
if self.buffer_hard_drop then
self.buffer_hard_drop = false
self:onHardDrop(self.piece.position.y - (
self.big_mode and
ruleset.big_spawn_positions[self.piece.shape].y or
ruleset.spawn_positions[self.piece.shape].y)
)
end
if self.buffer_soft_drop then
self.buffer_soft_drop = false
end
if self.lock_drop then if self.lock_drop then
self.drop_locked = true self.drop_locked = true
end end
@@ -415,11 +516,11 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
table.remove(self.next_queue, 1) table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
self:playNextSound() self:playNextSound(ruleset)
end end
function GameMode:playNextSound() function GameMode:playNextSound(ruleset)
playSE("blocks", self.next_queue[1].shape) playSE("blocks", ruleset.next_sounds[self.next_queue[1].shape])
end end
function GameMode:getHighScoreData() function GameMode:getHighScoreData()
@@ -451,8 +552,8 @@ function GameMode:drawNextQueue(ruleset)
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour] local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
function drawPiece(piece, skin, offsets, pos_x, pos_y) function drawPiece(piece, skin, offsets, pos_x, pos_y)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
local x = offset.x + ruleset.spawn_positions[piece].x local x = offset.x + ruleset.draw_offsets[piece].x + ruleset.spawn_positions[piece].x
local y = offset.y + 4.7 local y = offset.y + ruleset.draw_offsets[piece].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
@@ -468,8 +569,7 @@ function GameMode:drawNextQueue(ruleset)
end end
end end
if self.hold_queue ~= nil and self.enable_hold then if self.hold_queue ~= nil and self.enable_hold then
local hold_color = self.held and 0.6 or 1 self:setHoldOpacity()
self:setHoldOpacity(1, hold_color)
drawPiece( drawPiece(
self.hold_queue.shape, self.hold_queue.shape,
self.hold_queue.skin, self.hold_queue.skin,
@@ -480,15 +580,13 @@ function GameMode:drawNextQueue(ruleset)
return false return false
end end
function GameMode:setNextOpacity(i, j) function GameMode:setNextOpacity(i)
i = i ~= nil and i or 1 love.graphics.setColor(1, 1, 1, 1)
j = j ~= nil and j or 1
love.graphics.setColor(j, j, j, i)
end end
function GameMode:setHoldOpacity(i, j)
i = i ~= nil and i or 1 function GameMode:setHoldOpacity()
j = j ~= nil and j or 1 local colour = self.held and 0.6 or 1
love.graphics.setColor(j, j, j, i) love.graphics.setColor(colour, colour, colour, 1)
end end
function GameMode:drawScoringInfo() function GameMode:drawScoringInfo()
@@ -528,7 +626,8 @@ function GameMode:sectionColourFunction(section)
return { 1, 1, 1, 1 } return { 1, 1, 1, 1 }
end end
function GameMode:drawSectionTimesWithSecondary(current_section) function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
section_limit = section_limit or math.huge
local section_x = 530 local section_x = 530
local section_secondary_x = 440 local section_secondary_x = 440
@@ -553,7 +652,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
current_x = section_secondary_x current_x = section_secondary_x
end end
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left") if current_section <= section_limit then
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
end
end end
function GameMode:drawSectionTimesWithSplits(current_section) function GameMode:drawSectionTimesWithSplits(current_section)

View File

@@ -256,8 +256,10 @@ end
local function getSectionForLevel(level) local function getSectionForLevel(level)
if level < 2001 then if level < 2001 then
return math.floor(level / 100) + 1 return math.floor(level / 100) + 1
else elseif level < 2020 then
return 20 return 20
else
return 21
end end
end end
@@ -442,7 +444,7 @@ function Marathon2020Game:drawScoringInfo()
love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left") love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left")
self:drawSectionTimesWithSecondary(current_section) self:drawSectionTimesWithSecondary(current_section, 20)
if (self.cool_timer > 0) then if (self.cool_timer > 0) then
love.graphics.printf("COOL!!", 64, 400, 160, "center") love.graphics.printf("COOL!!", 64, 400, 160, "center")

View File

@@ -2,6 +2,7 @@ require 'funcs'
local GameMode = require 'tetris.modes.gamemode' local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Grid = require 'tetris.components.grid'
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls' local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'

View File

@@ -26,6 +26,7 @@ function MarathonA2Game:new()
self.section_start_time = 0 self.section_start_time = 0
self.section_times = { [0] = 0 } self.section_times = { [0] = 0 }
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
self.tetris_count = 0
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
@@ -130,6 +131,9 @@ end
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
if not self.clear then if not self.clear then
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
@@ -145,6 +149,7 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
end end
function MarathonA2Game:onLineClear(cleared_row_count) function MarathonA2Game:onLineClear(cleared_row_count)
self:updateSectionTimes(self.level, self.level + cleared_row_count)
self.level = math.min(self.level + cleared_row_count, 999) self.level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 and not self.clear then if self.level == 999 and not self.clear then
self.clear = true self.clear = true
@@ -164,6 +169,9 @@ function MarathonA2Game:updateSectionTimes(old_level, new_level)
section_time = self.frames - self.section_start_time section_time = self.frames - self.section_start_time
self.section_times[math.floor(old_level / 100)] = section_time self.section_times[math.floor(old_level / 100)] = section_time
self.section_start_time = self.frames self.section_start_time = self.frames
self.section_tetrises[math.floor(old_level / 100)] = self.tetris_count
self.tetris_count = 0
print(self.section_tetrises[math.floor(old_level / 100)])
end end
end end
@@ -231,14 +239,16 @@ local grade_conversion = {
17, 18, 19 17, 18, 19
} }
function MarathonA2Game:whilePieceActive()
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
self.grade_point_decay_counter = 0
self.grade_points = math.max(0, self.grade_points - 1)
end
end
function MarathonA2Game:updateGrade(cleared_lines) function MarathonA2Game:updateGrade(cleared_lines)
if self.clear then return end if self.clear or cleared_lines == 0 then return
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 else
self.grade_points = self.grade_points + ( self.grade_points = self.grade_points + (
math.ceil( math.ceil(

View File

@@ -313,13 +313,16 @@ local grade_conversion = {
17 17
} }
function MarathonA3Game:whilePieceActive()
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
self.grade_point_decay_counter = 0
self.grade_points = math.max(0, self.grade_points - 1)
end
end
function MarathonA3Game:updateGrade(cleared_lines) function MarathonA3Game:updateGrade(cleared_lines)
if cleared_lines == 0 then if cleared_lines == 0 then return
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 else
if self.clear then if self.clear then
if self:qualifiesForMRoll() then if self:qualifiesForMRoll() then
@@ -424,8 +427,8 @@ function MarathonA3Game:drawScoringInfo()
end end
-- draw section time data -- draw section time data
current_section = math.floor(self.level / 100) + 1 current_section = self.level >= 999 and 11 or math.floor(self.level / 100) + 1
self:drawSectionTimesWithSecondary(current_section) self:drawSectionTimesWithSecondary(current_section, 10)
--[[ --[[
section_x = 530 section_x = 530

View File

@@ -86,12 +86,8 @@ function PhantomMania2Game:getGarbageLimit()
else return 8 end else return 8 end
end end
function PhantomMania2Game:getNextPiece(ruleset) function PhantomMania2Game: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 PhantomMania2Game:hitTorikan(old_level, new_level) function PhantomMania2Game:hitTorikan(old_level, new_level)

View File

@@ -4,6 +4,7 @@ local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local SakuraRandomizer = require 'tetris.randomizers.sakura' local SakuraRandomizer = require 'tetris.randomizers.sakura'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SakuraGame = GameMode:extend() local SakuraGame = GameMode:extend()
@@ -264,10 +265,14 @@ local maps = {
local STAGE_TRANSITION_TIME = 300 local STAGE_TRANSITION_TIME = 300
function SakuraGame:new() function SakuraGame:new(secret_inputs)
self.super:new() self.super:new()
self.randomizer = SakuraRandomizer() self.randomizer = (
(
secret_inputs.rotate_left and secret_inputs.rotate_right
) and History6RollsRandomizer() or SakuraRandomizer()
)
self.current_map = 1 self.current_map = 1
self.time_limit = 10800 self.time_limit = 10800
@@ -403,18 +408,24 @@ function SakuraGame:advanceOneFrame(inputs, ruleset)
self.frames = self.frames + 1 self.frames = self.frames + 1
self.stage_frames = self.stage_frames + 1 self.stage_frames = self.stage_frames + 1
self.time_limit = self.time_limit - 1 self.time_limit = math.max(self.time_limit - 1, 0)
if self.time_limit <= 0 then self.game_over = true end if self.time_limit <= 0 and self.piece == nil then
self.game_over = true
end
if self.piece ~= nil and self.frames % 30 == 0 and if self.piece ~= nil and
effects[self.current_map] == "roll" effects[self.current_map] == "roll" and
self.stage_pieces % 4 == 0
then then
ruleset:attemptRotate( self.piece.colour = "F"
{[config.gamesettings.world_reverse == 3 or if self.stage_frames % 30 == 0 then
(ruleset.world and config.gamesettings.world_reverse == 2) ruleset:attemptRotate(
and "rotate_left" or "rotate_right"] = true}, {[config.gamesettings.world_reverse == 3 or
self.piece, self.grid, false (ruleset.world and config.gamesettings.world_reverse == 2)
) and "rotate_left" or "rotate_right"] = true},
self.piece, self.grid, false
)
end
end end
else else
self.cleared_frames = STAGE_TRANSITION_TIME self.cleared_frames = STAGE_TRANSITION_TIME
@@ -482,13 +493,17 @@ function SakuraGame:drawScoringInfo()
if effects[self.current_map] then if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left") love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
end 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.setFont(font_3x5_3)
love.graphics.setColor( love.graphics.setColor(
(self.time_limit % 4 < 2 and (self.time_limit % 4 < 2 and
self.time_limit <= frameTime(0,10) and self.time_limit <= frameTime(0,10) and
self.grid:hasGemBlocks() and self.grid:hasGemBlocks() and
self.time_limit ~= 0) and self.time_limit ~= 0 and
self.ready_frames == 0) and
{ 1, 0.3, 0.3, 1 } or { 1, 0.3, 0.3, 1 } or
{ 1, 1, 1, 1 } { 1, 1, 1, 1 }
) )

View File

@@ -66,24 +66,24 @@ function SurvivalA1Game:getGravity()
end end
local function getRankForScore(score) local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400} if score < 400 then return {rank = "9", next = 400, grade = 0}
elseif score < 800 then return {rank = "8", next = 800} elseif score < 800 then return {rank = "8", next = 800, grade = 1}
elseif score < 1400 then return {rank = "7", next = 1400} elseif score < 1400 then return {rank = "7", next = 1400, grade = 2}
elseif score < 2000 then return {rank = "6", next = 2000} elseif score < 2000 then return {rank = "6", next = 2000, grade = 3}
elseif score < 3500 then return {rank = "5", next = 3500} elseif score < 3500 then return {rank = "5", next = 3500, grade = 4}
elseif score < 5500 then return {rank = "4", next = 5500} elseif score < 5500 then return {rank = "4", next = 5500, grade = 5}
elseif score < 8000 then return {rank = "3", next = 8000} elseif score < 8000 then return {rank = "3", next = 8000, grade = 6}
elseif score < 12000 then return {rank = "2", next = 12000} elseif score < 12000 then return {rank = "2", next = 12000, grade = 7}
elseif score < 16000 then return {rank = "1", next = 16000} elseif score < 16000 then return {rank = "1", next = 16000, grade = 8}
elseif score < 22000 then return {rank = "S1", next = 22000} elseif score < 22000 then return {rank = "S1", next = 22000, grade = 9}
elseif score < 30000 then return {rank = "S2", next = 30000} elseif score < 30000 then return {rank = "S2", next = 30000, grade = 10}
elseif score < 40000 then return {rank = "S3", next = 40000} elseif score < 40000 then return {rank = "S3", next = 40000, grade = 11}
elseif score < 52000 then return {rank = "S4", next = 52000} elseif score < 52000 then return {rank = "S4", next = 52000, grade = 12}
elseif score < 66000 then return {rank = "S5", next = 66000} elseif score < 66000 then return {rank = "S5", next = 66000, grade = 13}
elseif score < 82000 then return {rank = "S6", next = 82000} elseif score < 82000 then return {rank = "S6", next = 82000, grade = 14}
elseif score < 100000 then return {rank = "S7", next = 100000} elseif score < 100000 then return {rank = "S7", next = 100000, grade = 15}
elseif score < 120000 then return {rank = "S8", next = 120000} elseif score < 120000 then return {rank = "S8", next = 120000, grade = 16}
else return {rank = "S9", next = "???"} else return {rank = "S9", next = "???", grade = 17}
end end
end end
@@ -208,10 +208,15 @@ end
function SurvivalA1Game:getHighscoreData() function SurvivalA1Game:getHighscoreData()
return { return {
grade = self.grade, grade = (
(self.gm_conditions["level300"] and
self.gm_conditions["level500"] and
self.gm_conditions["level999"]) and
18 or getRankForScore(self.score).grade
),
frames = self.frames,
score = self.score, score = self.score,
level = self.level, level = self.level,
frames = self.frames,
} }
end end

View File

@@ -92,12 +92,8 @@ function SurvivalA3Game:getGarbageLimit()
else return 8 end else return 8 end
end end
function SurvivalA3Game:getNextPiece(ruleset) function SurvivalA3Game: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 SurvivalA3Game:hitTorikan(old_level, new_level) function SurvivalA3Game:hitTorikan(old_level, new_level)

View File

@@ -0,0 +1,23 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local BagRandomizer = Randomizer:extend()
function BagRandomizer:new(pieces)
self.bag = {}
self.pieces = pieces
for i = 1, self.pieces do
table.insert(self.bag, i)
end
end
function BagRandomizer:generatePiece()
if next(self.bag) == nil then
for i = 1, self.pieces do
table.insert(self.bag, i)
end
end
local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x)
end
return BagRandomizer

View File

@@ -3,6 +3,7 @@ local Object = require 'libs.classic'
local Randomizer = Object:extend() local Randomizer = Object:extend()
function Randomizer:new() function Randomizer:new()
self.possible_pieces = 7
self:initialize() self:initialize()
end end

View File

@@ -19,26 +19,6 @@ ARS.colourscheme = {
ARS.softdrop_lock = false ARS.softdrop_lock = false
ARS.harddrop_lock = true ARS.harddrop_lock = true
ARS.spawn_positions = {
I = { x=5, y=2 },
J = { x=4, y=3 },
L = { x=4, y=3 },
O = { x=5, y=3 },
S = { x=4, y=3 },
T = { x=4, y=3 },
Z = { x=4, y=3 },
}
ARS.big_spawn_positions = {
I = { x=3, y=0 },
J = { x=2, y=1 },
L = { x=2, y=1 },
O = { x=3, y=1 },
S = { x=2, y=1 },
T = { x=2, y=1 },
Z = { x=2, y=1 },
}
function ARS:onPieceCreate(piece, grid) function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0 piece.floorkick = 0
piece.manipulations = 0 piece.manipulations = 0

View File

@@ -6,26 +6,6 @@ local ARS = Ruleset:extend()
ARS.name = "ACE-ARS2" ARS.name = "ACE-ARS2"
ARS.hash = "ArikaACE2" ARS.hash = "ArikaACE2"
ARS.spawn_positions = {
I = { x=5, y=2 },
J = { x=4, y=3 },
L = { x=4, y=3 },
O = { x=5, y=3 },
S = { x=4, y=3 },
T = { x=4, y=3 },
Z = { x=4, y=3 },
}
ARS.big_spawn_positions = {
I = { x=3, y=0 },
J = { x=2, y=1 },
L = { x=2, y=1 },
O = { x=3, y=1 },
S = { x=2, y=1 },
T = { x=2, y=1 },
Z = { x=2, y=1 },
}
function ARS:onPieceCreate(piece, grid) function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0 piece.floorkick = 0
piece.manipulations = 0 piece.manipulations = 0

View File

@@ -8,26 +8,6 @@ SRS.hash = "ACE Standard"
SRS.MANIPULATIONS_MAX = 128 SRS.MANIPULATIONS_MAX = 128
SRS.spawn_positions = {
I = { x=5, y=2 },
J = { x=4, y=3 },
L = { x=4, y=3 },
O = { x=5, y=3 },
S = { x=4, y=3 },
T = { x=4, y=3 },
Z = { x=4, y=3 },
}
SRS.big_spawn_positions = {
I = { x=3, y=0 },
J = { x=2, y=1 },
L = { x=2, y=1 },
O = { x=3, y=1 },
S = { x=2, y=1 },
T = { x=2, y=1 },
Z = { x=2, y=1 },
}
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then

275
tetris/rulesets/pairs.lua Normal file
View File

@@ -0,0 +1,275 @@
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

@@ -22,10 +22,34 @@ 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.next_sounds = {
I = "I",
L = "L",
J = "J",
S = "S",
Z = "Z",
O = "O",
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
-- Component functions. -- Component functions.
function Ruleset:new() function Ruleset:new()
if config.gamesettings.piece_colour == 1 then if config.gamesettings.piece_colour == 1 then
blocks["bone"] = (not self.world) and blocks["bone"] = (not self.world) and
{ {
@@ -88,8 +112,14 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
end end
end end
local was_drop_blocked = piece:isDropBlocked(grid)
self:attemptRotate(new_inputs, piece, grid, initial) self:attemptRotate(new_inputs, piece, grid, initial)
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
end
-- prev_inputs becomes the previous inputs -- prev_inputs becomes the previous inputs
for input, value in pairs(inputs) do for input, value in pairs(inputs) do
prev_inputs[input] = inputs[input] prev_inputs[input] = inputs[input]
@@ -130,17 +160,21 @@ end
function Ruleset:movePiece(piece, grid, move, instant) function Ruleset:movePiece(piece, grid, move, instant)
local x = piece.position.x local x = piece.position.x
local was_drop_blocked = piece:isDropBlocked(grid)
if move == "left" then if move == "left" then
piece:moveInGrid({x=-1, y=0}, 1, grid, false) piece:moveInGrid({x=-1, y=0}, 1, grid, false)
elseif move == "right" then elseif move == "right" then
piece:moveInGrid({x=1, y=0}, 1, grid, false) piece:moveInGrid({x=1, y=0}, 1, grid, false)
elseif move == "speedleft" then elseif move == "speedleft" then
piece:moveInGrid({x=-1, y=0}, 10, grid, instant) piece:moveInGrid({x=-1, y=0}, grid.width, grid, instant)
elseif move == "speedright" then elseif move == "speedright" then
piece:moveInGrid({x=1, y=0}, 10, grid, instant) piece:moveInGrid({x=1, y=0}, grid.width, grid, instant)
end end
if piece.position.x ~= x then if piece.position.x ~= x then
self:onPieceMove(piece, grid) self:onPieceMove(piece, grid)
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
end
end end
end end
@@ -148,6 +182,11 @@ 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
) )
if piece.big then
gravity = gravity / 2
drop_speed = drop_speed / 2
end
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
@@ -181,7 +220,9 @@ function Ruleset:getDefaultOrientation() return 1 end
function Ruleset:initializePiece( function Ruleset:initializePiece(
inputs, data, grid, gravity, prev_inputs, inputs, data, grid, gravity, prev_inputs,
move, lock_delay, drop_speed, move, lock_delay, drop_speed,
drop_locked, hard_drop_locked, big, irs drop_locked, hard_drop_locked, big, irs,
buffer_hard_drop, buffer_soft_drop,
lock_on_hard_drop, lock_on_soft_drop
) )
local spawn_positions local spawn_positions
if big then if big then
@@ -191,21 +232,42 @@ function Ruleset:initializePiece(
end end
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour] local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
local spawn_x
if (grid.width ~= 10) then
local percent = spawn_positions[data.shape].x / 10
for i = 0, grid.width - 1 do
if i / grid.width >= percent then
spawn_x = i
break
end
end
end
local spawn_dy = (
config.gamesettings.spawn_positions == 2 and
2 or 0
)
local piece = Piece(data.shape, data.orientation - 1, { local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_positions[data.shape].x, x = spawn_x and spawn_x or spawn_positions[data.shape].x,
y = spawn_positions[data.shape].y 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)
self:onPieceCreate(piece) self:onPieceCreate(piece)
if irs then if irs then
if inputs.rotate_left or inputs.rotate_left2 or self:rotatePiece(inputs, piece, grid, {}, true)
inputs.rotate_right or inputs.rotate_right2 or if (data.orientation - 1) ~= piece.rotation then
inputs.rotate_180 then
playSE("irs") playSE("irs")
end end
self:rotatePiece(inputs, piece, grid, {}, true)
end end
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked) self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
if (buffer_hard_drop and config.gamesettings.buffer_lock == 1) then
piece:dropToBottom(grid)
if lock_on_hard_drop then piece.locked = true end
end
if (buffer_soft_drop and lock_on_soft_drop and piece:isDropBlocked(grid) and config.gamesettings.buffer_lock == 1) then
piece.locked = true
end
return piece return piece
end end

View File

@@ -10,7 +10,7 @@ SRS.enable_IRS_wallkicks = true
SRS.MANIPULATIONS_MAX = 15 SRS.MANIPULATIONS_MAX = 15
function SRS:check_new_low(piece) function SRS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y local y = piece.position.y + block.y
if y > piece.lowest_y then if y > piece.lowest_y then
@@ -76,7 +76,7 @@ function SRS:onPieceCreate(piece, grid)
end end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
self:check_new_low(piece) self:checkNewLow(piece)
if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
else else
@@ -96,7 +96,7 @@ end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:check_new_low(piece) self:checkNewLow(piece)
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= self.MANIPULATIONS_MAX then if piece.manipulations >= self.MANIPULATIONS_MAX then
piece:moveInGrid({ x = 0, y = 1 }, 1, grid) piece:moveInGrid({ x = 0, y = 1 }, 1, grid)