Compare commits

..

37 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
26 changed files with 4409 additions and 241 deletions

View File

@@ -139,4 +139,34 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE 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)
-- returns a string representing a number with commas as thousands separator (e.g. 12,345,678)
local s = string.format("%d", number)
local pos = string.len(s) % 3
if pos == 0 then pos = 3 end
local s
if type(number) == "number" then
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)
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
end
@@ -66,4 +76,13 @@ end
function Mod1(n, m)
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
return ((n-1) % m) + 1
end
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"),
A = 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.bgm"
require "load.save"
require "load.bigint"
loadSave()
require "scene"
--config["side_next"] = false
@@ -15,8 +16,10 @@ function love.load()
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.arr then config.arr = 2 end
if not config.dcd then config.dcd = 0 end
if not config.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end

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

@@ -50,9 +50,37 @@ function GameScene:render()
)
-- 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.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:drawPiece()
@@ -76,15 +104,22 @@ function GameScene:render()
love.graphics.setFont(font_3x5_3)
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
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_hash = self.game.hash .. "-" .. self.ruleset.hash
submitHighscore(highscore_hash, highscore_entry)
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
elseif e.input == "retry" then
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
self.paused = not self.paused
@@ -104,8 +139,19 @@ function GameScene:onInputRelease(e)
end
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
table.insert(highscores[hash], data)
if isHighscore(data, highscores[hash]) then
highscores[hash] = data
end
saveHighscores()
end

View File

@@ -11,6 +11,7 @@ ConfigScene.options = {
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
{"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"}},
{"das_last_key", "DAS Switch", false, {"Default", "Instant"}},
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
@@ -32,8 +33,8 @@ function ConfigScene:new()
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.bgmSlider = newSlider(465, 375, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_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, 400, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
end
function ConfigScene:update()
@@ -58,7 +59,7 @@ function ConfigScene:render()
if not ConfigScene.options[self.highlight][3] then
love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
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
love.graphics.setFont(font_3x5_2)
@@ -75,8 +76,8 @@ function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_3)
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 325)
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 325)
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 345)
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 345)
love.graphics.setColor(1, 1, 1, 0.75)
self.sfxSlider:draw()

View File

@@ -41,14 +41,14 @@ function ModeSelectScene:render()
elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.25)
end
love.graphics.rectangle("fill", 20, 258, 240, 22)
love.graphics.rectangle("fill", 20, 198, 240, 22)
if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.25)
elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.5)
end
love.graphics.rectangle("fill", 340, 258, 200, 22)
love.graphics.rectangle("fill", 340, 198, 200, 22)
love.graphics.setColor(1, 1, 1, 1)
@@ -56,13 +56,36 @@ function ModeSelectScene:render()
love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
if(idx >= self.menu_state.mode-6 and idx <= self.menu_state.mode+6) then
love.graphics.printf(mode.name, 40, (200 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
end
end
for idx, ruleset in pairs(rulesets) do
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
if(idx >= self.menu_state.ruleset-6 and idx <= self.menu_state.ruleset+6) then
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

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}
{"das", "DAS", "dasSlider"},
{"arr", "ARR", "arrSlider"},
{"dcd", "DCD", "dcdSlider"},
}
local optioncount = #TuningScene.options
@@ -20,13 +21,15 @@ function TuningScene:new()
})
self.highlight = 1
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20})
self.arrSlider = newSlider(290, 325, 400, config.arr, 0, 6, function(v) config.arr = 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, 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
function TuningScene:update()
self.dasSlider:update()
self.arrSlider:update()
self.arrSlider:update()
self.dcdSlider:update()
end
function TuningScene:render()
@@ -38,7 +41,7 @@ function TuningScene:render()
)
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)
@@ -50,11 +53,13 @@ function TuningScene:render()
love.graphics.setFont(font_3x5_3)
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)
self.dasSlider:draw()
self.arrSlider:draw()
self.arrSlider:draw()
self.dcdSlider:draw()
end
function TuningScene:onInputPress(e)

View File

@@ -6,13 +6,15 @@ local empty = { skin = "", colour = "" }
local oob = { skin = "", colour = "" }
local block = { skin = "2tie", colour = "A" }
function Grid:new()
function Grid:new(width, height)
self.grid = {}
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_age[y] = {}
for x = 1, 10 do
for x = 1, self.width do
self.grid[y][x] = empty
self.grid_age[y][x] = 0
end
@@ -20,8 +22,8 @@ function Grid:new()
end
function Grid:clear()
for y = 1, 24 do
for x = 1, 10 do
for y = 1, self.height do
for x = 1, self.width do
self.grid[y][x] = empty
self.grid_age[y][x] = 0
end
@@ -29,7 +31,7 @@ function Grid:clear()
end
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
else return self.grid[y][x]
end
@@ -98,18 +100,20 @@ end
function Grid:getClearedRowCount()
local count = 0
for row = 1, 24 do
local cleared_row_table = {}
for row = 1, self.height do
if self:isRowFull(row) then
count = count + 1
table.insert(cleared_row_table, row)
end
end
return count
return count, cleared_row_table
end
function Grid:markClearedRows()
for row = 1, 24 do
for row = 1, self.height do
if self:isRowFull(row) then
for x = 1, 10 do
for x = 1, self.width do
self.grid[row][x] = {
skin = self.grid[row][x].skin,
colour = "X"
@@ -122,66 +126,52 @@ function Grid:markClearedRows()
end
function Grid:clearClearedRows()
for row = 1, 24 do
for row = 1, self.height do
if self:isRowFull(row) then
for above_row = row, 2, -1 do
self.grid[above_row] = self.grid[above_row - 1]
self.grid_age[above_row] = self.grid_age[above_row - 1]
end
self.grid[1] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
self.grid[1] = {}
self.grid_age[1] = {}
for i = 1, self.width do
self.grid[1][i] = empty
self.grid_age[1][i] = 0
end
end
end
return true
end
function Grid:copyBottomRow()
for row = 1, 23 do
for row = 1, self.height - 1 do
self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1]
end
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for col = 1, 10 do
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
self.grid[self.height] = {}
self.grid_age[self.height] = {}
for i = 1, self.width do
self.grid[self.height][i] = (self.grid[self.height - 1][i] == empty) and empty or block
self.grid_age[self.height][i] = 0
end
return true
end
function Grid:garbageRise(row_vals)
for row = 1, 23 do
self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1]
end
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for col = 1, 10 do
self.grid[24][col] = (row_vals[col] == "e") and empty or block
for row = 1, self.height - 1 do
self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1]
end
end
function Grid:applyFourWide()
for row = 1, 24 do
local x = self.grid[row]
x[1] = x[1]~=block and block or x[1]
x[2] = x[2]~=block and block or x[2]
x[3] = x[3]~=block and block or x[3]
x[8] = x[8]~=block and block or x[8]
x[9] = x[9]~=block and block or x[9]
x[10] = x[10]~=block and block or x[10]
end
end
function Grid:applyCeiling(lines)
for row = 1, lines do
for col = 1, 9 do
self.grid[row][col] = block
end
self.grid[self.height] = {}
self.grid_age[self.height] = {}
for i = 1, self.width do
self.grid[self.height][i] = (row_vals[i] == "e") and empty or block
self.grid_age[self.height][i] = 0
end
end
function Grid:clearSpecificRow(row)
for col = 1, 10 do
for col = 1, self.width do
self.grid[row][col] = empty
end
end
@@ -195,7 +185,7 @@ function Grid:applyPiece(piece)
for index, offset in pairs(offsets) do
x = piece.position.x + offset.x
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] = {
skin = piece.skin,
colour = piece.colour
@@ -211,7 +201,7 @@ function Grid:applyBigPiece(piece)
y = piece.position.y + offset.y
for a = 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] = {
skin = piece.skin,
colour = piece.colour
@@ -223,8 +213,8 @@ function Grid:applyBigPiece(piece)
end
function Grid:checkForBravo(cleared_row_count)
for i = 0, 23 - cleared_row_count do
for j = 0, 9 do
for i = 0, self.height - 1 - cleared_row_count do
for j = 0, self.width - 1 do
if self:isOccupied(j, i) then return false end
end
end
@@ -232,9 +222,9 @@ function Grid:checkForBravo(cleared_row_count)
end
function Grid:checkStackHeight()
for i = 0, 23 do
for j = 0, 9 do
if self:isOccupied(j, i) then return 24 - i end
for i = 0, self.height - 1 do
for j = 0, self.width - 1 do
if self:isOccupied(j, i) then return self.height - i end
end
end
return 0
@@ -275,8 +265,8 @@ function Grid:checkSecretGrade()
end
function Grid:hasGemBlocks()
for y = 1, 24 do
for x = 1, 10 do
for y = 1, self.height do
for x = 1, self.width do
if self.grid[y][x].skin == "gem" then
return true
end
@@ -287,16 +277,16 @@ end
function Grid:mirror()
local new_grid = {}
for y = 1, 24 do
for y = 1, self.height do
new_grid[y] = {}
for x = 1, 10 do
for x = 1, self.width do
new_grid[y][x] = empty
end
end
for y = 1, 24 do
for x = 1, 10 do
new_grid[y][x] = self.grid[y][11 - x]
for y = 1, self.height do
for x = 1, self.width do
new_grid[y][x] = self.grid[y][self.width + 1 - x]
end
end
self.grid = new_grid
@@ -311,9 +301,83 @@ function Grid:applyMap(map)
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()
for y = 1, 24 do
for x = 1, 10 do
for y = 1, self.height do
for x = 1, self.width do
if self.grid[y][x] ~= empty then
self.grid_age[y][x] = self.grid_age[y][x] + 1
end
@@ -322,8 +386,8 @@ function Grid:update()
end
function Grid:draw()
for y = 5, 24 do
for x = 1, 10 do
for y = 5, self.height do
for x = 1, self.width do
if self.grid[y][x] ~= empty then
if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1)
@@ -344,14 +408,14 @@ function Grid:draw()
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)
end
if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or self.grid[y+1][x].colour == "X") 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)
end
if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
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)
end
end
@@ -361,8 +425,8 @@ function Grid:draw()
end
function Grid:drawOutline()
for y = 5, 24 do
for x = 1, 10 do
for y = 5, self.height do
for x = 1, self.width do
if self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
@@ -373,14 +437,14 @@ function Grid:drawOutline()
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)
end
if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or self.grid[y+1][x].colour == "X") 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)
end
if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
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)
end
end
@@ -391,8 +455,8 @@ end
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
lock_flash = lock_flash == nil and true or lock_flash
brightness = brightness == nil and 0.5 or brightness
for y = 5, 24 do
for x = 1, 10 do
for y = 5, self.height do
for x = 1, self.width do
if self.grid[y][x] ~= empty then
if self.grid[y][x].colour == "X" then
opacity = 1 - self.grid_age[y][x] / 15
@@ -410,14 +474,14 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
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)
end
if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or self.grid[y+1][x].colour == "X") 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)
end
if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
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)
end
end
@@ -436,8 +500,8 @@ function Grid:drawCustom(colour_function, gamestate)
gamestate: the gamemode instance itself to pass in colour_function
]]
for y = 5, 24 do
for x = 1, 10 do
for y = 5, self.height do
for x = 1, self.width do
local block = self.grid[y][x]
if block ~= empty then
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
@@ -452,14 +516,14 @@ function Grid:drawCustom(colour_function, gamestate)
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)
end
if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or self.grid[y+1][x].colour == "X") 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)
end
if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
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)
end
end

View File

@@ -13,7 +13,7 @@ local GameMode = Object:extend()
GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new(secret_inputs)
self.grid = Grid()
self.grid = Grid(10, 24)
self.randomizer = Randomizer()
self.piece = nil
self.ready_frames = 100
@@ -22,6 +22,7 @@ function GameMode:new(secret_inputs)
self.score = 0
self.level = 0
self.lines = 0
self.squares = 0
self.drop_bonus = 0
self.are = 0
self.lcd = 0
@@ -46,6 +47,8 @@ function GameMode:new(secret_inputs)
self.big_mode = false
self.irs = true
self.ihs = true
self.square_mode = false
self.immobile_spin_bonus = false
self.rpc_details = "In game"
self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1",
@@ -72,6 +75,7 @@ function GameMode:getLineARE() return 25 end
function GameMode:getLockDelay() return 30 end
function GameMode:getLineClearDelay() return 40 end
function GameMode:getDasLimit() return 15 end
function GameMode:getDasCutDelay() return 0 end
function GameMode:getNextPiece(ruleset)
return {
@@ -105,14 +109,10 @@ function GameMode:initialize(ruleset, secret_inputs)
end
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
if self.game_over_frames >= 60 then
self.completed = true
end
return
end
if self.completed then return end
if config.gamesettings.diagonal_input == 2 then
if inputs["left"] or inputs["right"] then
@@ -127,16 +127,34 @@ function GameMode:update(inputs, ruleset)
-- advance one frame
if self:advanceOneFrame(inputs, ruleset) == false then return end
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
self:chargeDAS(inputs, self:getDasLimit(), self:getARR())
-- set attempt flags
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end
if inputs["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
inputs["rotate_left"] or inputs["rotate_right"] or
inputs["rotate_left2"] or inputs["rotate_right2"] or
inputs["rotate_180"]
then
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
if self.piece == nil then
@@ -160,7 +178,9 @@ function GameMode:update(inputs, ruleset)
self.hard_drop_locked = false
end
-- diff vars to use in checks
local piece_y = self.piece.position.y
local piece_rot = self.piece.rotation
ruleset:processPiece(
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
@@ -170,6 +190,22 @@ function GameMode:update(inputs, ruleset)
)
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
self.piece:isDropBlocked(self.grid) and
@@ -193,13 +229,15 @@ function GameMode:update(inputs, ruleset)
if self.piece.locked == true then
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()
self:onPieceLock(self.piece, cleared_row_count)
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
self.grid:markClearedRows()
self.piece = nil
if self.enable_hold then
self.held = false
@@ -263,6 +301,15 @@ end
function GameMode:onGameOver()
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
-- DAS functions
@@ -393,14 +440,15 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end
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)
else
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
end
self:onPieceEnter()
if not self.grid:canPlacePiece(self.piece) then
self:onGameOver()
self.game_over = true
end
end
@@ -437,7 +485,10 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
self.prev_inputs, self.move,
self:getLockDelay(), self:getDropSpeed(),
self.lock_drop, self.lock_hard_drop, self.big_mode,
self.irs, self.buffer_hard_drop, self.buffer_soft_drop,
(
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
@@ -501,8 +552,8 @@ function GameMode:drawNextQueue(ruleset)
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
function drawPiece(piece, skin, offsets, pos_x, pos_y)
for index, offset in pairs(offsets) do
local x = offset.x + ruleset.spawn_positions[piece].x
local y = offset.y + 4.7
local x = offset.x + ruleset.draw_offsets[piece].x + ruleset.spawn_positions[piece].x
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)
end
end
@@ -518,8 +569,7 @@ function GameMode:drawNextQueue(ruleset)
end
end
if self.hold_queue ~= nil and self.enable_hold then
local hold_color = self.held and 0.6 or 1
self:setHoldOpacity(1, hold_color)
self:setHoldOpacity()
drawPiece(
self.hold_queue.shape,
self.hold_queue.skin,
@@ -530,15 +580,13 @@ function GameMode:drawNextQueue(ruleset)
return false
end
function GameMode:setNextOpacity(i, j)
i = i ~= nil and i or 1
j = j ~= nil and j or 1
love.graphics.setColor(j, j, j, i)
function GameMode:setNextOpacity(i)
love.graphics.setColor(1, 1, 1, 1)
end
function GameMode:setHoldOpacity(i, j)
i = i ~= nil and i or 1
j = j ~= nil and j or 1
love.graphics.setColor(j, j, j, i)
function GameMode:setHoldOpacity()
local colour = self.held and 0.6 or 1
love.graphics.setColor(colour, colour, colour, 1)
end
function GameMode:drawScoringInfo()
@@ -578,7 +626,8 @@ function GameMode:sectionColourFunction(section)
return { 1, 1, 1, 1 }
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_secondary_x = 440
@@ -603,7 +652,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
current_x = section_secondary_x
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
function GameMode:drawSectionTimesWithSplits(current_section)

View File

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

View File

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

View File

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

View File

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

View File

@@ -493,7 +493,7 @@ function SakuraGame:drawScoringInfo()
if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
end
if self.randomizer.history then
if self.used_randomizer.history then
love.graphics.printf("RANDOM PIECES ACTIVE!", 240, 150, 200, "left")
end

View File

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

View File

@@ -19,26 +19,6 @@ ARS.colourscheme = {
ARS.softdrop_lock = false
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)
piece.floorkick = 0
piece.manipulations = 0

View File

@@ -6,26 +6,6 @@ local ARS = Ruleset:extend()
ARS.name = "ACE-ARS2"
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)
piece.floorkick = 0
piece.manipulations = 0

View File

@@ -8,26 +8,6 @@ SRS.hash = "ACE Standard"
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)
piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then

View File

@@ -48,6 +48,27 @@ PAIRS.big_spawn_positions = {
[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",

View File

@@ -34,6 +34,16 @@ Ruleset.next_sounds = {
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.
@@ -102,8 +112,14 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
end
end
local was_drop_blocked = piece:isDropBlocked(grid)
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
for input, value in pairs(inputs) do
prev_inputs[input] = inputs[input]
@@ -144,17 +160,21 @@ end
function Ruleset:movePiece(piece, grid, move, instant)
local x = piece.position.x
local was_drop_blocked = piece:isDropBlocked(grid)
if move == "left" then
piece:moveInGrid({x=-1, y=0}, 1, grid, false)
elseif move == "right" then
piece:moveInGrid({x=1, y=0}, 1, grid, false)
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
piece:moveInGrid({x=1, y=0}, 10, grid, instant)
piece:moveInGrid({x=1, y=0}, grid.width, grid, instant)
end
if piece.position.x ~= x then
self:onPieceMove(piece, grid)
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
end
end
end
@@ -162,6 +182,11 @@ function Ruleset:dropPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity
)
if piece.big then
gravity = gravity / 2
drop_speed = drop_speed / 2
end
local y = piece.position.y
if inputs["down"] == true and drop_locked == false then
if additive_gravity then
@@ -207,19 +232,33 @@ function Ruleset:initializePiece(
end
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, {
x = spawn_positions[data.shape].x,
y = spawn_positions[data.shape].y
x = spawn_x and spawn_x or spawn_positions[data.shape].x,
y = spawn_positions[data.shape].y - spawn_dy
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
self:onPieceCreate(piece)
if irs then
if inputs.rotate_left or inputs.rotate_left2 or
inputs.rotate_right or inputs.rotate_right2 or
inputs.rotate_180 then
self:rotatePiece(inputs, piece, grid, {}, true)
if (data.orientation - 1) ~= piece.rotation then
playSE("irs")
end
self:rotatePiece(inputs, piece, grid, {}, true)
end
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
if (buffer_hard_drop and config.gamesettings.buffer_lock == 1) then