Compare commits

..

45 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
83e498534c Merge branch 'master' of https://github.com/sashlilac/cambridge 2021-02-18 12:01:05 -05:00
Ishaan Bhardwaj
8f19c73e2a Simultaneous keyboard and joystick inputs implemented!
Implements #9!!!
2021-02-18 12:00:57 -05:00
Ishaan Bhardwaj
e36b855ff7 The Discord server is no longer sponsored by the project. 2021-02-18 10:42:19 -05:00
Ishaan Bhardwaj
23b58951cb World rule Survival A2 has a lenient torikan time 2021-02-17 22:46:33 -05:00
Ishaan Bhardwaj
1d73916b7c Arika-SRS rulesets no longer lock immediately 2021-02-17 18:29:14 -05:00
Ishaan Bhardwaj
3947e9f02f Fix the drop block lock rotation with SRS 2021-02-17 17:31:16 -05:00
Ishaan Bhardwaj
99b15803ee Adjusted 0 ARR to trigger onPieceMove multiple times 2021-02-17 17:21:51 -05:00
Ishaan Bhardwaj
d350b25726 Forgot to set guideline SRS to always rotate 2021-02-17 14:52:05 -05:00
Ishaan Bhardwaj
44e4d00172 Merge branch 'master' of https://github.com/sashlilac/cambridge 2021-02-17 14:48:42 -05:00
Ishaan Bhardwaj
31e2529265 Upward kicks for SRS count toward rotation limit 2021-02-17 14:48:35 -05:00
Ishaan Bhardwaj
ea7c75f0b3 Cambridge Discord Server temp. decommissioned
Please contact Milla#7746 on Discord for help.
2021-02-17 10:45:07 -05:00
Ishaan Bhardwaj
714c6b5e99 Floorkicks reworked (read comments)
If not classic lock, upward kicks reset to the top of the tile
2021-02-16 23:28:54 -05:00
Ishaan Bhardwaj
6a5d5a9c88 Fixed some modes' getNextPiece routines 2021-02-16 17:02:13 -05:00
Ishaan Bhardwaj
03491ba151 Strategy mode endgame nerfed 2021-02-16 16:57:31 -05:00
Ishaan Bhardwaj
6e22e3d15b Ti-ARS autolock fix 2021-02-16 16:19:51 -05:00
Ishaan Bhardwaj
66ab5992ad Added onPieceMove/Rotate/Drop for gamemodes 2021-02-16 15:27:57 -05:00
Ishaan Bhardwaj
2c07c2a58c BigInt changes, read extended description
Disabled strict type checking, can be re-enabled in bleeding edge. (This is done so bigint ops run faster)
Added a negation method and updated the corresponding metamethod to use it.
2021-02-16 13:03:53 -05:00
Ishaan Bhardwaj
a4d3f3bffc Update README.md 2021-02-16 13:00:07 -05:00
Ishaan Bhardwaj
9ac60cbb5e afterLineClear func added and splits time draw fix 2021-02-15 12:26:52 -05:00
Ishaan Bhardwaj
cdd846c3e6 Made the volume sliders scroll more consistently 2021-02-13 22:00:45 -05:00
Ishaan Bhardwaj
33d260b753 Removed the print statement from A2 2021-02-12 23:31:13 -05:00
Ishaan Bhardwaj
1644fcdf8e Bigint exponentiation by 1 now returns a clone 2021-02-12 10:05:04 -05:00
Ishaan Bhardwaj
f3c1cf6e1f Fixed an issue where DS-World wouldn't harddrop 2021-02-11 22:11:35 -05:00
Ishaan Bhardwaj
06a8a2ebf7 Mandate safelock on 0 ARE rulesets/modes 2021-02-11 22:08:52 -05:00
Ishaan Bhardwaj
15354ce004 dropToBottom no longer resets lock delay
it's already handled by the rulesets anyhow
2021-02-11 21:20:23 -05:00
Ishaan Bhardwaj
af02cd3467 Classic lock (GB/NES-like) added as a gamemode var 2021-02-11 15:46:56 -05:00
Ishaan Bhardwaj
acb05918c1 Custom line clear animations 2021-02-10 23:10:10 -05:00
Ishaan Bhardwaj
b644c8e457 Revert "Default line clear animation set to fadeout"
Please, reminder to self, TEST YOUR COMMITS.
This reverts commit 288961e12a.
2021-02-10 22:46:58 -05:00
Ishaan Bhardwaj
288961e12a Default line clear animation set to fadeout 2021-02-10 22:41:07 -05:00
Ishaan Bhardwaj
a047e51681 Framework for custom line clear animations added
Colored fadeout is the default
2021-02-10 18:35:51 -05:00
Ishaan Bhardwaj
77f24f5ee5 Human readable bigint output changes 2021-02-10 12:45:55 -05:00
Ishaan Bhardwaj
32c2274bef Optimized bigint exponentiation (again) 2021-02-10 11:38:10 -05:00
Ishaan Bhardwaj
4920e5de1c Added another type check to the bigint 2021-02-10 11:15:56 -05:00
Ishaan Bhardwaj
8418fc8ab7 Update README.md 2021-02-10 10:32:18 -05:00
Ishaan Bhardwaj
711a5120f1 Update README.md 2021-02-10 10:31:52 -05:00
Ishaan Bhardwaj
e7c3c9446a Cambridge banner looks better on dark theme now
Courtesy of @sinefuse
2021-02-10 09:05:10 -05:00
Ishaan Bhardwaj
3ac39acd7a Removed bigint comparison metamethods (read below)
Use bigint.compare from now on
2021-02-09 12:27:57 -05:00
Ishaan Bhardwaj
d0505251b3 Spawn positions now ruleset dependent
Is configurable in options
2021-02-08 23:23:50 -05:00
Ishaan Bhardwaj
bb0fe2ac20 BigInt now has a digits method (read comments)
Kind of unnecessary but included for completeness
2021-02-08 16:56:06 -05:00
Ishaan Bhardwaj
986ebac47f BigInt division fixed 2021-02-08 16:07:48 -05:00
Ishaan Bhardwaj
9799147f96 Revert "BigInt fixes and optimization (read comments)"
Apparently division *still* isn't being handled correctly.
This reverts commit 1dda12e4be.
2021-02-08 14:53:19 -05:00
Ishaan Bhardwaj
1dda12e4be BigInt fixes and optimization (read comments)
Fixed a nasty division bug where intermediate operations could result in negative zero. Optimized exponentiation to use exponentiation by squaring.
2021-02-08 14:10:34 -05:00
Ishaan Bhardwaj
38947e00c0 Added a tostring function for bigints 2021-02-08 10:34:47 -05:00
Ishaan Bhardwaj
035f6dd7b4 Fixed big division when (big1 < big2) 2021-02-08 10:23:10 -05:00
Ishaan Bhardwaj
aa3eadc93d Update README.md 2021-02-08 09:00:51 -05:00
20 changed files with 374 additions and 204 deletions

View File

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

View File

@@ -2,7 +2,7 @@
-- 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 strict = false
--------------------------------------------------------------------------------
@@ -33,12 +33,7 @@ function bigint.new(num)
return bigint.add(lhs, rhs)
end,
__unm = function()
if (self.sign == "+") then
self.sign = "-"
else
self.sign = "+"
end
return self
return bigint.negate(self)
end,
__sub = function(lhs, rhs)
return bigint.subtract(lhs, rhs)
@@ -55,14 +50,8 @@ function bigint.new(num)
__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, "<=")
__tostring = function()
return bigint.unserialize(self, "s")
end
})
@@ -88,7 +77,8 @@ function bigint.check(big, force)
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")
assert(digit <= 9 and digit >= 0, digit .. " is not between 0 and 9")
assert(math.floor(digit) == digit, digit .. " is not an integer")
end
end
return true
@@ -103,6 +93,24 @@ function bigint.abs(big)
return result
end
-- Return a new big with the same digits but the opposite sign (negation)
function bigint.negate(big)
bigint.check(big)
local result = big:clone()
if (result.sign == "+") then
result.sign = "-"
else
result.sign = "+"
end
return result
end
-- Return the number of digits in the big
function bigint.digits(big)
bigint.check(big)
return #big.digits
end
-- Convert a big to a number or string
function bigint.unserialize(big, output_type, precision)
bigint.check(big)
@@ -137,7 +145,7 @@ function bigint.unserialize(big, output_type, precision)
-- Unserialization to human-readable form or scientific notation only
-- requires reading the first few digits
if (precision == nil) then
precision = 3
precision = math.min(#big.digits, 3)
else
assert(precision > 0, "Precision cannot be less than 1")
assert(math.floor(precision) == precision,
@@ -146,17 +154,18 @@ function bigint.unserialize(big, output_type, precision)
-- num is the first (precision + 1) digits, the first being separated by
-- a decimal point from the others
num = num .. big.digits[1]
num = num .. math.floor(big.digits[1])
if (precision > 1) then
num = num .. "."
for i = 1, (precision - 1) do
num = num .. big.digits[i + 1]
num = num .. math.floor(big.digits[i + 1])
end
end
if ((output_type == "human-readable")
or (output_type == "human")
or (output_type == "h")) then
or (output_type == "h"))
and (#big.digits >= 3 and #big.digits <= 10002) then
-- Human-readable output contributed by 123eee555
local name
@@ -435,7 +444,6 @@ function bigint.multiply(big1, big2)
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
@@ -446,13 +454,23 @@ function bigint.exponentiate(big, power)
if (bigint.compare(exp, bigint.new(0), "==")) then
return bigint.new(1)
elseif (bigint.compare(exp, bigint.new(1), "==")) then
return big
return big:clone()
else
local result = big:clone()
local result = bigint.new(1)
local base = big:clone()
while (bigint.compare(exp, bigint.new(1), ">")) do
result = bigint.multiply(result, big)
exp = bigint.subtract(exp, bigint.new(1))
while (true) do
if (bigint.compare(
bigint.modulus(exp, bigint.new(2)), bigint.new(1), "=="
)) then
result = bigint.multiply(result, base)
end
if (bigint.compare(exp, bigint.new(1), "==")) then
break
else
exp = bigint.divide(exp, bigint.new(2))
base = bigint.multiply(base, base)
end
end
return result
@@ -468,7 +486,7 @@ function bigint.divide_raw(big1, big2)
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)
return bigint.new(0), big1:clone()
else
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
assert(big1.sign == "+", "error: big1 is not positive")
@@ -476,54 +494,35 @@ function bigint.divide_raw(big1, big2)
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
local dividend = bigint.new() -- Dividend of a single operation
-- 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
local neg_zero = bigint.new(0)
neg_zero.sign = "-"
-- 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
for i = 1, #big1.digits do
-- Fixes a negative zero bug
if (#dividend.digits ~= 0) and (bigint.compare(dividend, neg_zero, "==")) then
dividend = bigint.new()
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
table.insert(dividend.digits, big1.digits[i])
local factor = bigint.new(0)
while bigint.compare(dividend, big2, ">=") do
dividend = bigint.subtract(dividend, big2)
factor = bigint.add(factor, bigint.new(1))
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()
for i = 0, #factor.digits - 1 do
result.digits[#result.digits + 1 - i] = factor.digits[i + 1]
end
end
-- Remove leading zeros from result
while (result.digits[1] == 0) do
table.remove(result.digits, 1)
end
-- The remainder of the final loop is returned as the function's
-- overall remainder
return result, dividend
end
end

View File

@@ -83,6 +83,7 @@ function GameScene:render()
end
self.game:drawGrid()
if self.game.lcd > 0 then self.game:drawLineClearAnimation() end
self.game:drawPiece()
self.game:drawNextQueue(self.ruleset)
self.game:drawScoringInfo()

View File

@@ -11,7 +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"}},
{"spawn_positions", "Spawn Positions", false, {"Per ruleset", "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"}},
@@ -103,7 +103,7 @@ function ConfigScene:onInputPress(e)
else
playSE("cursor")
sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 3) / (sld.max - sld.min)))
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 5) / (sld.max - sld.min)))
end
elseif e.input == "right" or e.scancode == "right" then
if not self.options[self.highlight][3] then
@@ -113,7 +113,7 @@ function ConfigScene:onInputPress(e)
else
playSE("cursor")
sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 3) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min))
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min))
end
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave()

View File

@@ -31,6 +31,7 @@ end
function ConfigScene:new()
self.input_state = 1
self.key = 1
self.set_inputs = newSetInputs()
self.new_input = {}
self.axis_timer = 0
@@ -62,7 +63,7 @@ function ConfigScene:render()
if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press key or joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
love.graphics.print("press " .. (self.key == 2 and "joystick" or "key") .. " input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
end
@@ -97,18 +98,28 @@ function ConfigScene:onInputPress(e)
self.new_input = {}
end
elseif e.scancode == "tab" then
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
self.set_inputs[configurable_inputs[self.input_state]] =
(
self.set_inputs[configurable_inputs[self.input_state]] == false
and "" or self.set_inputs[configurable_inputs[self.input_state]]
) ..
(self.key == 2 and " / " or "") .. "skipped"
if self.key == 2 then
self.input_state = self.input_state + 1
elseif e.scancode ~= "escape" then
self.key = 1
else
self.key = 2
end
elseif e.scancode ~= "escape" and self.key == 1 then
-- all other keys can be configured
if not self.new_input.keys then
self.new_input.keys = {}
end
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
self.new_input.keys[e.scancode] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.key = 2
end
elseif string.sub(e.type, 1, 3) == "joy" then
elseif string.sub(e.type, 1, 3) == "joy" and self.key == 2 then
if self.input_state <= table.getn(configurable_inputs) then
if e.type == "joybutton" then
addJoystick(self.new_input, e.name)
@@ -116,11 +127,13 @@ function ConfigScene:onInputPress(e)
self.new_input.joysticks[e.name].buttons = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jbtn " ..
self.set_inputs[configurable_inputs[self.input_state]] ..
" / jbtn " ..
e.button ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].buttons[e.button] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.key = 1
elseif e.type == "joyaxis" then
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
addJoystick(self.new_input, e.name)
@@ -131,11 +144,13 @@ function ConfigScene:onInputPress(e)
self.new_input.joysticks[e.name].axes[e.axis] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jaxis " ..
self.set_inputs[configurable_inputs[self.input_state]] ..
" / jaxis " ..
(e.value >= 1 and "+" or "-") .. e.axis ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.key = 1
self.last_axis = e.axis
self.axis_timer = 0
end
@@ -149,11 +164,13 @@ function ConfigScene:onInputPress(e)
self.new_input.joysticks[e.name].hats[e.hat] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jhat " ..
self.set_inputs[configurable_inputs[self.input_state]]
" / jhat " ..
e.hat .. " " .. e.direction ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.key = 1
end
end
end

View File

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

View File

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

View File

@@ -45,6 +45,7 @@ function GameMode:new(secret_inputs)
self.enable_hard_drop = true
self.next_queue_length = 1
self.additive_gravity = true
self.classic_lock = false
self.draw_section_times = false
self.draw_secondary_section_times = false
self.big_mode = false
@@ -63,6 +64,7 @@ function GameMode:new(secret_inputs)
self.hard_drop_locked = false
self.lock_on_soft_drop = false
self.lock_on_hard_drop = false
self.cleared_block_table = {}
self.used_randomizer = nil
self.hold_queue = nil
self.held = false
@@ -135,7 +137,7 @@ function GameMode:update(inputs, ruleset)
-- set attempt flags
if inputs["left"] or inputs["right"] then
self:onAttemptPieceMove(self.piece)
self:onAttemptPieceMove(self.piece, self.grid)
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
@@ -148,7 +150,7 @@ function GameMode:update(inputs, ruleset)
inputs["rotate_left2"] or inputs["rotate_right2"] or
inputs["rotate_180"]
then
self:onAttemptPieceRotate(self.piece)
self:onAttemptPieceRotate(self.piece, self.grid)
if self.immobile_spin_bonus and self.piece ~= nil then
if self.piece:isDropBlocked(self.grid) and
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
@@ -177,26 +179,32 @@ function GameMode:update(inputs, ruleset)
return
end
if self.lock_drop and inputs["down"] ~= true then
if (self.lock_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["down"] ~= true then
self.drop_locked = false
end
if self.lock_hard_drop and inputs["up"] ~= true then
if (self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
)) and inputs["up"] ~= true then
self.hard_drop_locked = false
end
-- diff vars to use in checks
local piece_y = self.piece.position.y
local piece_x = self.piece.position.x
local piece_rot = self.piece.rotation
ruleset:processPiece(
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
self.move, self:getLockDelay(), self:getDropSpeed(),
self.drop_locked, self.hard_drop_locked,
self.enable_hard_drop, self.additive_gravity
self.enable_hard_drop, self.additive_gravity, self.classic_lock
)
local piece_dy = self.piece.position.y - piece_y
local piece_dx = self.piece.position.x - piece_x
local piece_drot = self.piece.rotation - piece_rot
-- das cut
@@ -208,12 +216,13 @@ function GameMode:update(inputs, ruleset)
inputs.rotate_180
))
) then
self.das.frames = math.max(
self.das.frames - self:getDasCutDelay(),
-(self:getDasCutDelay() + 1)
)
self:dasCut()
end
if (piece_dx ~= 0) then self:onPieceMove(self.piece, self.grid) end
if (piece_drot ~= 0) then self:onPieceRotate(self.piece, self.grid) end
if (piece_dy ~= 0) then self:onPieceDrop(self.piece, self.grid) end
if inputs["up"] == true and
self.piece:isDropBlocked(self.grid) and
not self.hard_drop_locked then
@@ -244,7 +253,7 @@ function GameMode:update(inputs, ruleset)
self:onPieceLock(self.piece, cleared_row_count)
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
self.grid:markClearedRows()
self.cleared_block_table = self.grid:markClearedRows()
self.piece = nil
if self.enable_hold then
self.held = false
@@ -258,6 +267,7 @@ function GameMode:update(inputs, ruleset)
)
if self.lcd == 0 then
self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
if self.are == 0 then
self:initializeOrHold(inputs, ruleset)
end
@@ -287,13 +297,17 @@ end
-- event functions
function GameMode:whilePieceActive() end
function GameMode:onAttemptPieceMove(piece) end
function GameMode:onAttemptPieceRotate(piece) end
function GameMode:onAttemptPieceMove(piece, grid) end
function GameMode:onAttemptPieceRotate(piece, grid) end
function GameMode:onPieceMove(piece, grid) end
function GameMode:onPieceRotate(piece, grid) end
function GameMode:onPieceDrop(piece, grid) end
function GameMode:onPieceLock(piece, cleared_row_count)
playSE("lock")
end
function GameMode:onLineClear(cleared_row_count) end
function GameMode:afterLineClear(cleared_row_count) end
function GameMode:onPieceEnter() end
function GameMode:onHold() end
@@ -382,6 +396,13 @@ function GameMode:chargeDAS(inputs)
end
end
function GameMode:dasCut()
self.das.frames = math.max(
self.das.frames - self:getDasCutDelay(),
-(self:getDasCutDelay() + 1)
)
end
function GameMode:areCancel(inputs, ruleset)
if ruleset.are_cancel and self.piece_hard_dropped and
not self.prev_inputs.up and
@@ -425,7 +446,9 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
self.lcd = self.lcd - 1
self:areCancel(inputs, ruleset)
if self.lcd == 0 then
local cleared_row_count = self.grid:getClearedRowCount()
self.grid:clearClearedRows()
self:afterLineClear(cleared_row_count)
playSE("fall")
if self.are == 0 then
self:initializeOrHold(inputs, ruleset)
@@ -513,10 +536,14 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
if self.buffer_soft_drop then
self.buffer_soft_drop = false
end
if self.lock_drop then
if self.lock_drop or (
not ruleset.are or self:getARE() == 0
) then
self.drop_locked = true
end
if self.lock_hard_drop then
if self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
) then
self.hard_drop_locked = true
end
if generate_next_piece == nil then
@@ -536,14 +563,87 @@ function GameMode:getHighScoreData()
}
end
function GameMode:animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self:getLineClearDelay()),
skin, colour,
48 + x * 16, y * 16
}
end
function GameMode:drawLineClearAnimation()
-- animation function
-- params: block x, y, skin, colour
-- returns: table with RGBA, skin, colour, x, y
-- Fadeout (default)
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
-0.25 + 1.25 * (self.lcd / self:getLineClearDelay()),
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- Flash
--[[
function animation(x, y, skin, colour)
return {
1, 1, 1,
self.lcd % 6 < 3 and 1 or 0.25,
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- TGM1 pop-out
--[[
function animation(x, y, skin, colour)
local p = 0.5
local l = (
(self:getLineClearDelay() - self.lcd) / self:getLineClearDelay()
)
local dx = l * (x - (1 + self.grid.width) / 2)
local dy = l * (y - (1 + self.grid.height) / 2)
return {
1, 1, 1, 1, skin, colour,
48 + (x + dx) * 16,
(y + dy) * 16 + (464 / (p - 1)) * l * (p - l)
}
end
--]]
for y, row in pairs(self.cleared_block_table) do
for x, block in pairs(row) do
local animation_table = self:animation(x, y, block.skin, block.colour)
love.graphics.setColor(
animation_table[1], animation_table[2],
animation_table[3], animation_table[4]
)
love.graphics.draw(
blocks[animation_table[5]][animation_table[6]],
animation_table[7], animation_table[8]
)
end
end
end
function GameMode:drawPiece()
if self.piece ~= nil then
self.piece:draw(
1,
self:getLockDelay() == 0 and 1 or
(0.25 + 0.75 * math.max(1 - self.piece.gravity, 1 - (self.piece.lock_delay / self:getLockDelay()))),
self.grid
local b = (
self.classic_lock and
(
self.piece:isDropBlocked(self.grid) and
1 - self.piece.gravity or 1
) or
1 - (self.piece.lock_delay / self:getLockDelay())
)
self.piece:draw(1, 0.25 + 0.75 * b, self.grid)
end
end
@@ -676,7 +776,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
end
end
function GameMode:drawSectionTimesWithSplits(current_section)
function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
section_limit = section_limit or math.huge
local section_x = 440
local split_x = 530
@@ -684,15 +786,19 @@ function GameMode:drawSectionTimesWithSplits(current_section)
for section, time in pairs(self.section_times) do
if section > 0 then
love.graphics.setColor(self:sectionColourFunction(section))
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
love.graphics.setColor(1, 1, 1, 1)
split_time = split_time + time
love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left")
end
end
if (current_section <= section_limit) then
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left")
end
end
function GameMode:drawCustom() end

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,7 @@ ARS.colourscheme = {
ARS.softdrop_lock = false
ARS.harddrop_lock = true
ARS.spawn_above_field = true
function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0

View File

@@ -5,6 +5,7 @@ local ARS = Ruleset:extend()
ARS.name = "ACE-ARS2"
ARS.hash = "ArikaACE2"
ARS.spawn_above_field = true
function ARS:onPieceCreate(piece, grid)
piece.floorkick = 0

View File

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

View File

@@ -96,6 +96,9 @@ end
function ARS:onPieceRotate(piece, grid)
if piece.floorkick >= 1 then
piece.floorkick = piece.floorkick + 1
if piece:isDropBlocked(grid) then
piece.locked = true
end
end
end

View File

@@ -23,6 +23,7 @@ Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false
Ruleset.are_cancel = false
Ruleset.are = true
Ruleset.spawn_above_field = false
Ruleset.next_sounds = {
I = "I",
@@ -114,7 +115,9 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
local was_drop_blocked = piece:isDropBlocked(grid)
if self:canPieceRotate(piece, grid) then
self:attemptRotate(new_inputs, piece, grid, initial)
end
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
@@ -159,28 +162,43 @@ function Ruleset:attemptWallkicks(piece, new_piece, rot_dir, grid)
end
function Ruleset:movePiece(piece, grid, move, instant)
local x = piece.position.x
if not self:canPieceMove(piece, grid) then return end
local was_drop_blocked = piece:isDropBlocked(grid)
local offset = ({x=0, y=0})
local moves = 0
if move == "left" then
piece:moveInGrid({x=-1, y=0}, 1, grid, false)
offset.x = -1
moves = 1
elseif move == "right" then
piece:moveInGrid({x=1, y=0}, 1, grid, false)
offset.x = 1
moves = 1
elseif move == "speedleft" then
piece:moveInGrid({x=-1, y=0}, grid.width, grid, instant)
offset.x = -1
moves = grid.width
elseif move == "speedright" then
piece:moveInGrid({x=1, y=0}, grid.width, grid, instant)
offset.x = 1
moves = grid.width
end
for i = 1, moves do
local x = piece.position.x
if moves ~= 1 then
piece:moveInGrid(offset, 1, grid, instant)
else
piece:moveInGrid(offset, 1, grid, false)
end
if piece.position.x ~= x then
self:onPieceMove(piece, grid)
if piece.locked then break end
end
end
if not was_drop_blocked and piece:isDropBlocked(grid) then
playSE("bottom")
end
end
end
function Ruleset:dropPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity
hard_drop_enabled, additive_gravity, classic_lock
)
if piece.big then
gravity = gravity / 2
@@ -190,26 +208,29 @@ function Ruleset:dropPiece(
local y = piece.position.y
if inputs["down"] == true and drop_locked == false then
if additive_gravity then
piece:addGravity(gravity + drop_speed, grid)
piece:addGravity(gravity + drop_speed, grid, classic_lock)
else
piece:addGravity(math.max(gravity, drop_speed), grid)
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
end
elseif inputs["up"] == true and hard_drop_enabled == true then
if hard_drop_locked == true or piece:isDropBlocked(grid) then
piece:addGravity(gravity, grid)
piece:addGravity(gravity, grid, classic_lock)
else
piece:dropToBottom(grid)
end
else
piece:addGravity(gravity, grid)
piece:addGravity(gravity, grid, classic_lock)
end
if piece.position.y ~= y then
self:onPieceDrop(piece, grid)
end
end
function Ruleset:lockPiece(piece, grid, lock_delay)
if piece:isDropBlocked(grid) and piece.gravity >= 1 and piece.lock_delay >= lock_delay then
function Ruleset:lockPiece(piece, grid, lock_delay, classic_lock)
if piece:isDropBlocked(grid) and (
(classic_lock and piece.gravity >= 1) or
(not classic_lock and piece.lock_delay >= lock_delay)
) then
piece.locked = true
end
end
@@ -243,10 +264,17 @@ function Ruleset:initializePiece(
end
end
local spawn_dy = (
config.gamesettings.spawn_positions == 2 and
local spawn_dy
if (config.gamesettings.spawn_positions == 1) then
spawn_dy = (
self.spawn_above_field and 2 or 0
)
else
spawn_dy = (
config.gamesettings.spawn_positions == 3 and
2 or 0
)
end
local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_x and spawn_x or spawn_positions[data.shape].x,
@@ -278,7 +306,7 @@ function Ruleset:processPiece(
inputs, piece, grid, gravity, prev_inputs,
move, lock_delay, drop_speed,
drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity
hard_drop_enabled, additive_gravity, classic_lock
)
local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed]
@@ -292,11 +320,13 @@ function Ruleset:processPiece(
end
self:dropPiece(
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
hard_drop_enabled, additive_gravity
hard_drop_enabled, additive_gravity, classic_lock
)
self:lockPiece(piece, grid, lock_delay)
self:lockPiece(piece, grid, lock_delay, classic_lock)
end
function Ruleset:canPieceMove(piece, grid) return true end
function Ruleset:canPieceRotate(piece, grid) return true end
function Ruleset:onPieceMove(piece) end
function Ruleset:onPieceRotate(piece) end
function Ruleset:onPieceDrop(piece) end

View File

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

View File

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

View File

@@ -150,9 +150,9 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
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)
self:onPieceRotate(piece, grid, offset.y < 0)
return
end
end
@@ -182,16 +182,20 @@ function SRS:onPieceMove(piece, grid)
end
end
function SRS:onPieceRotate(piece, grid)
function SRS:onPieceRotate(piece, grid, upward)
piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then
if upward or piece:isDropBlocked(grid) then
piece.rotations = piece.rotations + 1
if piece.rotations >= self.ROTATIONS_MAX then
if piece.rotations >= self.ROTATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true
end
end
end
function SRS:canPieceRotate(piece)
return piece.rotations < self.ROTATIONS_MAX
end
function SRS:get180RotationValue() return 3 end
return SRS