mirror of
https://github.com/SashLilac/cambridge.git
synced 2025-05-13 20:21:25 -05:00
Compare commits
71 Commits
ae1231c47a
...
v0.3-beta7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffd808e6a0 | ||
|
|
dd96db170e | ||
|
|
7fa547c307 | ||
| b2d0838f90 | |||
|
|
42375cb2b8 | ||
|
|
fe162ed215 | ||
|
|
dda116f00f | ||
|
|
2d3aeeb47d | ||
|
|
784c768c57 | ||
|
|
c18e7ed244 | ||
|
|
9df6bb9989 | ||
|
|
f5873c97bc | ||
|
|
fabdad056e | ||
|
|
0e82a8758c | ||
|
|
e78df19112 | ||
|
|
49775b9578 | ||
|
|
6a3c6ecac0 | ||
|
|
90cf2ebef5 | ||
|
|
799a905a9c | ||
|
|
985f73c39d | ||
|
|
b5db5bbdc3 | ||
|
|
438acde2e2 | ||
|
|
0e1f40ad30 | ||
|
|
6cf6568a57 | ||
|
|
dafc113038 | ||
|
|
923f3d3696 | ||
|
|
db4132bf31 | ||
|
|
c58018dd51 | ||
|
|
c7d0034f9b | ||
|
|
ed5ea72e66 | ||
|
|
dc3ad825dc | ||
|
|
40cba83003 | ||
| a1b3f73787 | |||
|
|
4243d6b2ba | ||
| 33b3ad2889 | |||
|
|
adab1df480 | ||
|
|
711fa830a3 | ||
|
|
c434a3406b | ||
|
|
769b5043e3 | ||
|
|
713c62d807 | ||
|
|
c3f6e34518 | ||
|
|
4d0f6ab9fc | ||
|
|
594aa2620f | ||
|
|
199b535f70 | ||
|
|
9fbfbd5cda | ||
|
|
c5c4c4d95c | ||
|
|
53c51c2062 | ||
|
|
e4eb9972e6 | ||
|
|
7dbfe23059 | ||
|
|
61d5410f22 | ||
|
|
2cb0416548 | ||
| 83f3e297ce | |||
|
|
8fb01dc9a8 | ||
|
|
61de3c6dbf | ||
|
|
3c718c38e4 | ||
|
|
d18c3e298d | ||
|
|
33934bfb53 | ||
|
|
3e2d107687 | ||
|
|
312b95728d | ||
|
|
5013443302 | ||
|
|
a8ac8f5966 | ||
|
|
a5032386e6 | ||
|
|
264255290d | ||
|
|
a5839bede2 | ||
|
|
4ebf24316a | ||
|
|
f2acab4496 | ||
| 929069c1b6 | |||
|
|
3f2b38f7b3 | ||
|
|
56fb5aebea | ||
|
|
6c201596b0 | ||
|
|
50466c5902 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
*.sav
|
||||
*.love
|
||||
*.zip
|
||||
dist/*.zip
|
||||
dist/**/cambridge.exe
|
||||
dist/**/libs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2019 Joe Zeng
|
||||
Copyright (c) 2018-2021 Joe Zeng, Ishaan Bhardwaj
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
43
README.md
43
README.md
@@ -11,18 +11,6 @@ The Discord server has been reopened! https://discord.gg/AADZUmgsph
|
||||
|
||||
The game also has a website now with more detail than seen on this README: https://t-sp.in/cambridge
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for being my co-dev!
|
||||
- [joezeng](https://github.com/joezeng) for the original project, and for offering to help with the expansion!
|
||||
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||
- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
|
||||
|
||||
More special thanks can be found in-game, under the "Credits" menu.
|
||||
|
||||

|
||||
|
||||
Playing the game
|
||||
----------------
|
||||
|
||||
@@ -90,3 +78,34 @@ community, as well as borrowed from other places, either with licensing
|
||||
or as placeholders until suitable material can be found that is properly
|
||||
licensed. Their original sources, and copyright notices if applicable, are
|
||||
listed in the file SOURCES.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for being my co-dev!
|
||||
- [joezeng](https://github.com/joezeng) for the original project, and for offering to help with the expansion!
|
||||
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||
- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
|
||||
|
||||
More special thanks can be found in-game, under the "Credits" menu.
|
||||
|
||||
Other Notable Games
|
||||
-------------------
|
||||
|
||||
- [TGMsim](https://github.com/2Tie/TGMsim) by 2Tie
|
||||
- [Multimino](https://gamejolt.com/games/multimino/556683) by Axel Fox
|
||||
- [Tetra Legends](https://tetralegends.app) by Dr Ocelot
|
||||
- [ZTrix](https://discord.gg/MGhqCBDGNH) by Electra
|
||||
- [Shiromino](https://github.com/shiromino/shiromino) by Felicity/nightmareci/kdex
|
||||
- [Cursed Blocks](https://github.com/Manabender/Cursed-Blocks) by Manabender
|
||||
- [Picoris 2](https://www.lexaloffle.com/bbs/?tid=41733) by MarkGamed
|
||||
- [Tetra Online](https://github.com/Juan-Cartes/Tetra-Offline) by Mine
|
||||
- [Techmino](https://discord.gg/6Yuww44tq8) by MrZ
|
||||
- [Example Block Game](https://github.com/oshisaure/example-block-game) by Oshisaure
|
||||
- [TETR.IO](https://tetr.io) by osk
|
||||
- [Master of Blocks](https://discord.gg/72FZ49mjWh) by Phoenix Flare
|
||||
- [Spirit Drop](https://rayblastgames.com/spiritdrop.php) by RayRay26
|
||||
- [Puzzle Trial](https://kagamine-rin.itch.io/puzzle-trial) by Rin
|
||||
- [stackfuse](https://github.com/sinefuse/stackfuse) by sinefuse
|
||||
|
||||

|
||||
|
||||
48
SOURCES.md
48
SOURCES.md
@@ -170,3 +170,51 @@ 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.
|
||||
|
||||
discord-rpc (https://github.com/discord/discord-rpc)
|
||||
--------------------
|
||||
|
||||
Copyright 2017 Discord, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND 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.
|
||||
|
||||
lua-discordRPC (https://github.com/pfirsich/lua-discordRPC)
|
||||
--------------------
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Joel Schumacher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND 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.
|
||||
@@ -7,5 +7,11 @@
|
||||
@del dist\win32\SOURCES.md
|
||||
@del dist\win32\LICENSE.md
|
||||
@rmdir /Q /S dist\win32\libs
|
||||
@del dist\other\cambridge.love
|
||||
@del dist\other\SOURCES.md
|
||||
@del dist\other\LICENSE.md
|
||||
@rmdir /Q /S dist\other\libs
|
||||
@rmdir /Q /S dist\other
|
||||
@del dist\cambridge-windows.zip
|
||||
@del dist\cambridge-win32.zip
|
||||
@del dist\cambridge-other.zip
|
||||
18
funcs.lua
18
funcs.lua
@@ -1,10 +1,20 @@
|
||||
function copy(t)
|
||||
-- returns deep copy of t (as opposed to the shallow copy you get from var = t)
|
||||
-- returns top-layer shallow copy of t
|
||||
if type(t) ~= "table" then return t end
|
||||
local meta = getmetatable(t)
|
||||
local target = {}
|
||||
for k, v in pairs(t) do target[k] = v end
|
||||
setmetatable(target, meta)
|
||||
for k, v in next, t do target[k] = v end
|
||||
setmetatable(target, getmetatable(t))
|
||||
return target
|
||||
end
|
||||
|
||||
function deepcopy(t)
|
||||
-- returns infinite-layer deep copy of t
|
||||
if type(t) ~= "table" then return t end
|
||||
local target = {}
|
||||
for k, v in next, t do
|
||||
target[deepcopy(k)] = deepcopy(v)
|
||||
end
|
||||
setmetatable(target, deepcopy(getmetatable(t)))
|
||||
return target
|
||||
end
|
||||
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
-- 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 = false
|
||||
local strict = true
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local bigint = {}
|
||||
setmetatable(bigint, {__call = function(_, arg) return bigint.new(arg) end})
|
||||
|
||||
local mt = {
|
||||
__add = function(lhs, rhs)
|
||||
return bigint.add(lhs, rhs)
|
||||
end,
|
||||
__unm = function()
|
||||
return bigint.negate(self)
|
||||
__unm = function(arg)
|
||||
return bigint.negate(arg)
|
||||
end,
|
||||
__sub = function(lhs, rhs)
|
||||
return bigint.subtract(lhs, rhs)
|
||||
@@ -30,8 +31,8 @@ local mt = {
|
||||
__pow = function(lhs, rhs)
|
||||
return bigint.exponentiate(lhs, rhs)
|
||||
end,
|
||||
__tostring = function()
|
||||
return bigint.unserialize(self, "s")
|
||||
__tostring = function(arg)
|
||||
return bigint.unserialize(arg, "s")
|
||||
end,
|
||||
__eq = function(lhs, rhs)
|
||||
return bigint.compare(lhs, rhs, "==")
|
||||
@@ -76,7 +77,7 @@ function bigint.new(num)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
return bigint.strip(self)
|
||||
end
|
||||
|
||||
-- Check the type of a big
|
||||
@@ -96,6 +97,14 @@ function bigint.check(big, force)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Strip leading zeroes from a big, but don't remove the last zero
|
||||
function bigint.strip(big)
|
||||
while (#big.digits > 1) and (big.digits[1] == 0) do
|
||||
table.remove(big.digits, 1)
|
||||
end
|
||||
return big
|
||||
end
|
||||
|
||||
-- Return a new big with the same digits but with a positive sign (absolute
|
||||
-- value)
|
||||
function bigint.abs(big)
|
||||
@@ -329,12 +338,7 @@ function bigint.subtract_raw(big1, big2)
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- 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
|
||||
return bigint.strip(result)
|
||||
end
|
||||
|
||||
-- FRONTEND: Addition and subtraction operations, accounting for signs
|
||||
@@ -364,6 +368,7 @@ function bigint.add(big1, big2)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function bigint.subtract(big1, big2)
|
||||
-- Type checking is done by bigint.compare in bigint.add
|
||||
-- Subtracting is like adding a negative
|
||||
@@ -460,7 +465,7 @@ end
|
||||
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")
|
||||
"negative powers are not supported")
|
||||
local exp = power:clone()
|
||||
|
||||
if (bigint.compare(exp, bigint.new(0), "==")) then
|
||||
@@ -530,12 +535,7 @@ function bigint.divide_raw(big1, big2)
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove leading zeros from result
|
||||
while (result.digits[1] == 0) do
|
||||
table.remove(result.digits, 1)
|
||||
end
|
||||
|
||||
return result, dividend
|
||||
return bigint.strip(result), dividend
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -24,6 +24,59 @@ if osname == "Linux" then
|
||||
elseif osname == "OS X" then
|
||||
discordRPClib = ffi.load(source.."/libs/discord-rpc.dylib")
|
||||
elseif osname == "Windows" then
|
||||
-- I would strongly advise never touching this. It was not trivial to get correct. -nightmareci
|
||||
|
||||
ffi.cdef[[
|
||||
typedef uint32_t DWORD;
|
||||
typedef char CHAR;
|
||||
typedef CHAR *LPSTR;
|
||||
typedef const CHAR *LPCSTR;
|
||||
typedef wchar_t WCHAR;
|
||||
typedef WCHAR *LPWSTR;
|
||||
typedef LPWSTR PWSTR;
|
||||
typedef const WCHAR *LPCWSTR;
|
||||
|
||||
static const DWORD CP_UTF8 = 65001;
|
||||
int32_t MultiByteToWideChar(
|
||||
DWORD CodePage,
|
||||
DWORD dwFlags,
|
||||
LPCSTR lpMultiByteStr,
|
||||
int32_t cbMultiByte,
|
||||
LPWSTR lpWideCharStr,
|
||||
int32_t cchWideChar
|
||||
);
|
||||
|
||||
int32_t WideCharToMultiByte(
|
||||
DWORD CodePage,
|
||||
DWORD dwFlags,
|
||||
LPCWSTR lpWideCharStr,
|
||||
int32_t cchWideChar,
|
||||
LPSTR lpMultiByteStr,
|
||||
int32_t cbMultiByte,
|
||||
void* lpDefaultChar,
|
||||
void* lpUsedDefaultChar
|
||||
);
|
||||
|
||||
DWORD GetShortPathNameW(
|
||||
LPCWSTR lpszLongPath,
|
||||
LPWSTR lpszShortPath,
|
||||
DWORD cchBuffer
|
||||
);
|
||||
]]
|
||||
|
||||
local originalWideSize = ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, nil, 0)
|
||||
local originalWide = ffi.new('WCHAR[?]', originalWideSize)
|
||||
ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, originalWide, originalWideSize)
|
||||
|
||||
local sourceSize = ffi.C.GetShortPathNameW(originalWide, nil, 0)
|
||||
local sourceWide = ffi.new('WCHAR[?]', sourceSize)
|
||||
ffi.C.GetShortPathNameW(originalWide, sourceWide, sourceSize)
|
||||
|
||||
local sourceChar = ffi.new('char[?]', sourceSize)
|
||||
ffi.C.WideCharToMultiByte(ffi.C.CP_UTF8, 0, sourceWide, sourceSize, sourceChar, sourceSize, nil, nil)
|
||||
|
||||
source = ffi.string(sourceChar)
|
||||
|
||||
discordRPClib = ffi.load(source.."/libs/discord-rpc.dll")
|
||||
else
|
||||
-- Else it crashes later on
|
||||
|
||||
35
load/bgm.lua
35
load/bgm.lua
@@ -7,33 +7,41 @@ bgm = {
|
||||
|
||||
local current_bgm = nil
|
||||
local bgm_locked = false
|
||||
local unfocused = false
|
||||
|
||||
function switchBGM(sound, subsound)
|
||||
if bgm_locked then return end
|
||||
if current_bgm ~= nil then
|
||||
current_bgm:stop()
|
||||
end
|
||||
if bgm_locked or config.bgm_volume <= 0 then
|
||||
current_bgm = nil
|
||||
elseif sound ~= nil then
|
||||
if subsound ~= nil then
|
||||
current_bgm = bgm[sound][subsound]
|
||||
resetBGMFadeout()
|
||||
elseif sound ~= nil then
|
||||
else
|
||||
current_bgm = bgm[sound]
|
||||
resetBGMFadeout()
|
||||
end
|
||||
else
|
||||
current_bgm = nil
|
||||
end
|
||||
if current_bgm ~= nil then
|
||||
resetBGMFadeout()
|
||||
end
|
||||
end
|
||||
|
||||
function switchBGMLoop(sound, subsound)
|
||||
if bgm_locked then return end
|
||||
switchBGM(sound, subsound)
|
||||
current_bgm:setLooping(true)
|
||||
if current_bgm then current_bgm:setLooping(true) end
|
||||
end
|
||||
|
||||
function lockBGM()
|
||||
bgm_locked = true
|
||||
end
|
||||
|
||||
function unlockBGM()
|
||||
bgm_locked = false
|
||||
end
|
||||
|
||||
local fading_bgm = false
|
||||
local fadeout_time = 0
|
||||
local total_fadeout_time = 0
|
||||
@@ -49,11 +57,11 @@ end
|
||||
function resetBGMFadeout(time)
|
||||
current_bgm:setVolume(config.bgm_volume)
|
||||
fading_bgm = false
|
||||
current_bgm:play()
|
||||
resumeBGM()
|
||||
end
|
||||
|
||||
function processBGMFadeout(dt)
|
||||
if fading_bgm then
|
||||
if current_bgm and fading_bgm then
|
||||
fadeout_time = fadeout_time - dt
|
||||
if fadeout_time < 0 then
|
||||
fadeout_time = 0
|
||||
@@ -63,13 +71,20 @@ function processBGMFadeout(dt)
|
||||
end
|
||||
end
|
||||
|
||||
function pauseBGM()
|
||||
function pauseBGM(f)
|
||||
if f then
|
||||
unfocused = true
|
||||
end
|
||||
if current_bgm ~= nil then
|
||||
current_bgm:pause()
|
||||
end
|
||||
end
|
||||
|
||||
function resumeBGM()
|
||||
function resumeBGM(f)
|
||||
if f and scene.paused and unfocused then
|
||||
unfocused = false
|
||||
return
|
||||
end
|
||||
if current_bgm ~= nil then
|
||||
current_bgm:play()
|
||||
end
|
||||
|
||||
@@ -25,6 +25,15 @@ backgrounds = {
|
||||
game_config = love.graphics.newImage("res/backgrounds/options-game.png"),
|
||||
}
|
||||
|
||||
-- in order, the colors are:
|
||||
-- red, orange, yellow, green, cyan, blue
|
||||
-- magenta (or purple), white, black
|
||||
-- the next three don't have colors tied to them
|
||||
-- F is used for lock flash
|
||||
-- A is a garbage block
|
||||
-- X is an invisible "block"
|
||||
-- don't use these for piece colors when making a ruleset
|
||||
-- all the others are fine to use
|
||||
blocks = {
|
||||
["2tie"] = {
|
||||
R = love.graphics.newImage("res/img/s1.png"),
|
||||
@@ -34,6 +43,8 @@ blocks = {
|
||||
C = love.graphics.newImage("res/img/s2.png"),
|
||||
B = love.graphics.newImage("res/img/s4.png"),
|
||||
M = love.graphics.newImage("res/img/s5.png"),
|
||||
W = love.graphics.newImage("res/img/s9.png"),
|
||||
D = love.graphics.newImage("res/img/s8.png"),
|
||||
F = love.graphics.newImage("res/img/s9.png"),
|
||||
A = love.graphics.newImage("res/img/s8.png"),
|
||||
X = love.graphics.newImage("res/img/s9.png"),
|
||||
@@ -46,6 +57,8 @@ blocks = {
|
||||
C = love.graphics.newImage("res/img/bone.png"),
|
||||
B = love.graphics.newImage("res/img/bone.png"),
|
||||
M = love.graphics.newImage("res/img/bone.png"),
|
||||
W = love.graphics.newImage("res/img/bone.png"),
|
||||
D = love.graphics.newImage("res/img/bone.png"),
|
||||
F = love.graphics.newImage("res/img/bone.png"),
|
||||
A = love.graphics.newImage("res/img/bone.png"),
|
||||
X = love.graphics.newImage("res/img/bone.png"),
|
||||
@@ -58,13 +71,16 @@ blocks = {
|
||||
C = love.graphics.newImage("res/img/gem2.png"),
|
||||
B = love.graphics.newImage("res/img/gem4.png"),
|
||||
M = love.graphics.newImage("res/img/gem5.png"),
|
||||
W = love.graphics.newImage("res/img/gem9.png"),
|
||||
D = love.graphics.newImage("res/img/gem9.png"),
|
||||
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"),
|
||||
W = love.graphics.newImage("res/img/squares.png"),
|
||||
Y = love.graphics.newImage("res/img/squareg.png"),
|
||||
F = love.graphics.newImage("res/img/squares.png"),
|
||||
X = love.graphics.newImage("res/img/squares.png"),
|
||||
}
|
||||
}
|
||||
@@ -87,7 +103,7 @@ ColourSchemes = {
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for name, blockset in pairs(blocks) do
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
local binser = require 'libs.binser'
|
||||
|
||||
function loadSave()
|
||||
local info = love.filesystem.getInfo(love.filesystem.getSaveDirectory())
|
||||
if not info or info.type ~= "directory" then
|
||||
love.filesystem.remove(love.filesystem.getSaveDirectory())
|
||||
love.filesystem.createDirectory(love.filesystem.getSaveDirectory())
|
||||
end
|
||||
config = loadFromFile(
|
||||
love.filesystem.getSaveDirectory() .. '/config.sav'
|
||||
)
|
||||
highscores = loadFromFile(
|
||||
love.filesystem.getSaveDirectory() .. '/highscores.sav'
|
||||
)
|
||||
config = loadFromFile('config.sav')
|
||||
highscores = loadFromFile('highscores.sav')
|
||||
end
|
||||
|
||||
function loadFromFile(filename)
|
||||
local save_data, len = binser.readFile(filename)
|
||||
local file_data = love.filesystem.read(filename)
|
||||
if file_data == nil then
|
||||
return {} -- new object
|
||||
end
|
||||
local save_data = binser.deserialize(file_data)
|
||||
if save_data == nil then
|
||||
return {} -- new object
|
||||
end
|
||||
@@ -49,13 +44,13 @@ function initConfig()
|
||||
end
|
||||
|
||||
function saveConfig()
|
||||
binser.writeFile(
|
||||
love.filesystem.getSaveDirectory() .. '/config.sav', config
|
||||
love.filesystem.write(
|
||||
'config.sav', binser.serialize(config)
|
||||
)
|
||||
end
|
||||
|
||||
function saveHighscores()
|
||||
binser.writeFile(
|
||||
love.filesystem.getSaveDirectory() .. '/highscores.sav', highscores
|
||||
love.filesystem.write(
|
||||
'highscores.sav', binser.serialize(highscores)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -27,33 +27,37 @@ sounds = {
|
||||
}
|
||||
|
||||
function playSE(sound, subsound)
|
||||
if subsound == nil then
|
||||
sounds[sound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound]:isPlaying() then
|
||||
sounds[sound]:stop()
|
||||
end
|
||||
sounds[sound]:play()
|
||||
else
|
||||
if sound ~= nil then
|
||||
if subsound ~= nil then
|
||||
sounds[sound][subsound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound][subsound]:isPlaying() then
|
||||
sounds[sound][subsound]:stop()
|
||||
end
|
||||
sounds[sound][subsound]:play()
|
||||
else
|
||||
sounds[sound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound]:isPlaying() then
|
||||
sounds[sound]:stop()
|
||||
end
|
||||
sounds[sound]:play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function playSEOnce(sound, subsound)
|
||||
if subsound == nil then
|
||||
sounds[sound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound]:isPlaying() then
|
||||
return
|
||||
end
|
||||
sounds[sound]:play()
|
||||
else
|
||||
if sound ~= nil then
|
||||
if subsound ~= nil then
|
||||
sounds[sound][subsound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound][subsound]:isPlaying() then
|
||||
return
|
||||
end
|
||||
sounds[sound][subsound]:play()
|
||||
else
|
||||
sounds[sound]:setVolume(config.sfx_volume)
|
||||
if sounds[sound]:isPlaying() then
|
||||
return
|
||||
end
|
||||
sounds[sound]:play()
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1 +1 @@
|
||||
version = "v0.3-beta5.2"
|
||||
version = "v0.3-beta6"
|
||||
21
main.lua
21
main.lua
@@ -10,6 +10,7 @@ function love.load()
|
||||
require "load.bigint"
|
||||
require "load.version"
|
||||
loadSave()
|
||||
require "funcs"
|
||||
require "scene"
|
||||
|
||||
--config["side_next"] = false
|
||||
@@ -36,14 +37,14 @@ function initModules()
|
||||
game_modes = {}
|
||||
mode_list = love.filesystem.getDirectoryItems("tetris/modes")
|
||||
for i=1,#mode_list do
|
||||
if(mode_list[i] ~= "gamemode.lua" and mode_list[i] ~= "unrefactored_modes") then
|
||||
if(mode_list[i] ~= "gamemode.lua" and string.sub(mode_list[i], -4) == ".lua") then
|
||||
game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5))
|
||||
end
|
||||
end
|
||||
rulesets = {}
|
||||
rule_list = love.filesystem.getDirectoryItems("tetris/rulesets")
|
||||
for i=1,#rule_list do
|
||||
if(rule_list[i] ~= "ruleset.lua" and rule_list[i] ~= "unrefactored_rulesets") then
|
||||
if(rule_list[i] ~= "ruleset.lua" and string.sub(rule_list[i], -4) == ".lua") then
|
||||
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
|
||||
end
|
||||
end
|
||||
@@ -82,7 +83,7 @@ end
|
||||
|
||||
function love.keypressed(key, scancode)
|
||||
-- global hotkeys
|
||||
if scancode == "f4" then
|
||||
if scancode == "f11" then
|
||||
config["fullscreen"] = not config["fullscreen"]
|
||||
saveConfig()
|
||||
love.window.setFullscreen(config["fullscreen"])
|
||||
@@ -99,8 +100,8 @@ function love.keypressed(key, scancode)
|
||||
-- f12 is reserved for saving screenshots
|
||||
elseif scancode == "f12" then
|
||||
local ss_name = os.date("ss/%Y-%m-%d_%H-%M-%S.png")
|
||||
local info = love.filesystem.getInfo("ss")
|
||||
if not info or info.type ~= "directory" then
|
||||
local info = love.filesystem.getInfo("ss", "directory")
|
||||
if not info then
|
||||
love.filesystem.remove("ss")
|
||||
love.filesystem.createDirectory("ss")
|
||||
end
|
||||
@@ -258,11 +259,15 @@ function love.joystickhat(joystick, hat, direction)
|
||||
end
|
||||
end
|
||||
|
||||
function love.wheelmoved(x, y)
|
||||
scene:onInputPress({input=nil, type="wheel", x=x, y=y})
|
||||
end
|
||||
|
||||
function love.focus(f)
|
||||
if f and (scene.title ~= "Game" or not scene.paused) then
|
||||
resumeBGM()
|
||||
if f then
|
||||
resumeBGM(true)
|
||||
else
|
||||
pauseBGM()
|
||||
pauseBGM(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
4
release
4
release
@@ -2,8 +2,10 @@
|
||||
mkdir dist
|
||||
mkdir dist/windows
|
||||
mkdir dist/win32
|
||||
cp cambridge.love dist/
|
||||
mkdir dist/other
|
||||
cat dist/windows/love.exe cambridge.love > dist/windows/cambridge.exe
|
||||
zip dist/cambridge-windows.zip dist/windows/* SOURCES.md LICENSE.md
|
||||
cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe
|
||||
zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE.md
|
||||
cp cambridge.love dist/other/
|
||||
zip dist/cambridge-other.zip cambridge.love libs/discord-rpc.* SOURCES.md LICENSE.md
|
||||
10
release.bat
10
release.bat
@@ -5,17 +5,23 @@ mkdir dist\windows
|
||||
mkdir dist\windows\libs
|
||||
mkdir dist\win32
|
||||
mkdir dist\win32\libs
|
||||
mkdir dist\other
|
||||
mkdir dist\other\libs
|
||||
|
||||
copy /b dist\windows\love.exe+cambridge.love dist\windows\cambridge.exe
|
||||
copy /b dist\win32\love.exe+cambridge.love dist\win32\cambridge.exe
|
||||
copy /b cambridge.love dist\other\cambridge.love
|
||||
|
||||
copy libs\discord-rpc.dll dist\windows\libs
|
||||
copy libs\discord-rpc.dll dist\win32\libs
|
||||
copy libs\discord-rpc.* dist\other\libs
|
||||
|
||||
copy SOURCES.md dist\windows
|
||||
copy LICENSE.md dist\windows
|
||||
copy SOURCES.md dist\win32
|
||||
copy LICENSE.md dist\win32
|
||||
copy SOURCES.md dist\other
|
||||
copy LICENSE.md dist\other
|
||||
|
||||
cd dist\windows
|
||||
tar -a -c -f ..\cambridge-windows.zip cambridge.exe *.dll libs *.md
|
||||
@@ -24,3 +30,7 @@ cd ..\..
|
||||
cd dist\win32
|
||||
tar -a -c -f ..\cambridge-win32.zip cambridge.exe *.dll libs *.md
|
||||
cd ..\..
|
||||
|
||||
cd dist\other
|
||||
tar -a -c -f ..\cambridge-other.zip cambridge.love libs *.md
|
||||
cd ..\..
|
||||
BIN
res/img/bone.png
BIN
res/img/bone.png
Binary file not shown.
|
Before Width: | Height: | Size: 188 B After Width: | Height: | Size: 151 B |
Binary file not shown.
|
Before Width: | Height: | Size: 153 B After Width: | Height: | Size: 151 B |
Binary file not shown.
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 1.1 KiB |
@@ -4,6 +4,8 @@ CreditsScene.title = "Credits"
|
||||
|
||||
function CreditsScene:new()
|
||||
self.frames = 0
|
||||
-- higher = slower
|
||||
self.scroll_speed = 1.85
|
||||
switchBGM("credit_roll", "gm3")
|
||||
end
|
||||
|
||||
@@ -11,16 +13,18 @@ function CreditsScene:update()
|
||||
if love.window.hasFocus() then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
if self.frames >= 4200 then
|
||||
if self.frames >= 2100 * self.scroll_speed then
|
||||
playSE("mode_decide")
|
||||
scene = TitleScene()
|
||||
switchBGM(nil)
|
||||
elseif self.frames == 3600 then
|
||||
elseif self.frames == math.floor(1950 * self.scroll_speed) then
|
||||
fadeoutBGM(2)
|
||||
end
|
||||
end
|
||||
|
||||
function CreditsScene:render()
|
||||
local offset = self.frames / self.scroll_speed
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds[19],
|
||||
@@ -29,36 +33,43 @@ function CreditsScene:render()
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2)
|
||||
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1770 - self.frames / 2, 240))
|
||||
love.graphics.print("Cambridge Credits", 320, 500 - offset)
|
||||
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(2030 - offset, 240))
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("Game Developers", 320, 550 - self.frames / 2)
|
||||
love.graphics.print("Project Heads", 320, 640 - self.frames / 2)
|
||||
love.graphics.print("Other Game Developers", 320, 730 - self.frames / 2)
|
||||
love.graphics.print("Special Thanks", 320, 950 - self.frames / 2)
|
||||
love.graphics.print("- Milla", 320, math.max(1850 - self.frames / 2, 320))
|
||||
love.graphics.print("Game Developers", 320, 550 - offset)
|
||||
love.graphics.print("Project Heads", 320, 640 - offset)
|
||||
love.graphics.print("Notable Game Developers", 320, 730 - offset)
|
||||
love.graphics.print("Special Thanks", 320, 1000 - offset)
|
||||
love.graphics.print("- Milla", 320, math.max(2110 - offset, 320))
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - self.frames / 2)
|
||||
love.graphics.print("Mizu\nHailey", 320, 680 - self.frames / 2)
|
||||
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - offset)
|
||||
love.graphics.print("Mizu\nMarkGamed", 320, 680 - offset)
|
||||
love.graphics.print(
|
||||
"Axel Fox - Multimino\nMine - Tetra Online\nDr Ocelot - Tetra Legends\n" ..
|
||||
"Felicity / nightmareci - Shiromino\n2Tie - TGMsim\nPhoenix Flare - Master of Blocks\n" ..
|
||||
"RayRay26 - Spirit Drop\nosk - TETR.IO\nMarkGamed7794 - Picoris 2",
|
||||
320, 770 - self.frames / 2
|
||||
"2Tie - TGMsim\nAxel Fox - Multimino\nDr Ocelot - Tetra Legends\n" ..
|
||||
"Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" ..
|
||||
"Mine - Tetra Online\nMrZ - Techmino\nosk - TETR.IO\n" ..
|
||||
"Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" ..
|
||||
"Rin - Puzzle Trial\nsinefuse - stackfuse",
|
||||
320, 770 - offset
|
||||
)
|
||||
love.graphics.print(
|
||||
"RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" ..
|
||||
"Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" ..
|
||||
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\n" ..
|
||||
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nArchina\nOliver\ncolour_thief\n" ..
|
||||
"Caithness\nkdex\nzid\nsaphie\nSuper302\nAurora\nswitchpalacecorner\nKitaru\n" ..
|
||||
"JBroms\nMany more I definitely missed!\n" ..
|
||||
"The Absolute PLUS Discord\nTetra Legends Discord\nTetra Online Discord\n" ..
|
||||
"Multimino Discord\nHard Drop Discord\nCambridge Discord\n" ..
|
||||
"321MrHaatz\nAdventium\nAgentBasey\nArchina\nAurora\n" ..
|
||||
"Caithness\nCheez\ncolour_thief\nCommando\nCublex\n" ..
|
||||
"CylinderKnot\neightsixfivezero\nEricICX\nGesomaru\n" ..
|
||||
"gizmo4487\nJBroms\nKirby703\nKitaru\n" ..
|
||||
"M1ssing0\nMattMayuga\nMyPasswordIsWeak\n" ..
|
||||
"Nikki Karissa\noffwo\nOliver\nPineapple\npokemonfan1937\n" ..
|
||||
"Pyra Neoxi\nRDST64\nRocketLanterns\nRustyFoxxo\n" ..
|
||||
"saphie\nShelleloch\nSimon\nstratus\nSuper302\n" ..
|
||||
"switchpalacecorner\nterpyderp\nTetrian22\nTetro48\nThatCookie\n" ..
|
||||
"TimmSkiller\nTrixciel\nuser74003\nZaptorZap\nZircean\n" ..
|
||||
"All other contributors and friends!\nThe Absolute PLUS Discord\n" ..
|
||||
"Tetra Legends Discord\nTetra Online Discord\nMultimino Discord\n" ..
|
||||
"Hard Drop Discord\nRusty's Systemspace\nCambridge Discord\n" ..
|
||||
"And to you, the player!",
|
||||
320, 990 - self.frames / 2
|
||||
320, 1040 - offset
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -42,79 +42,17 @@ function GameScene:update()
|
||||
end
|
||||
|
||||
function GameScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds[self.game:getBackground()],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
-- game frame
|
||||
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,
|
||||
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()
|
||||
if self.game.lcd > 0 then self.game:drawLineClearAnimation() end
|
||||
self.game:drawPiece()
|
||||
self.game:drawNextQueue(self.ruleset)
|
||||
self.game:drawScoringInfo()
|
||||
|
||||
-- ready/go graphics
|
||||
|
||||
if self.game.ready_frames <= 100 and self.game.ready_frames > 52 then
|
||||
love.graphics.draw(misc_graphics["ready"], 144 - 50, 240 - 14)
|
||||
elseif self.game.ready_frames <= 50 and self.game.ready_frames > 2 then
|
||||
love.graphics.draw(misc_graphics["go"], 144 - 27, 240 - 14)
|
||||
end
|
||||
|
||||
self.game:drawCustom()
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
if config.gamesettings.display_gamemode == 1 then
|
||||
love.graphics.printf(self.game.name .. " - " .. self.ruleset.name, 0, 460, 640, "left")
|
||||
end
|
||||
|
||||
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
|
||||
self.game:draw(self.paused)
|
||||
end
|
||||
|
||||
function GameScene:onInputPress(e)
|
||||
if (self.game.game_over or 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)
|
||||
|
||||
@@ -17,7 +17,7 @@ ConfigScene.options = {
|
||||
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
|
||||
{"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}},
|
||||
{"diagonal_input", "Diagonal Input", false, {"On", "Off"}},
|
||||
{"buffer_lock", "Buffer Lock Inputs", false, {"On", "Off"}},
|
||||
{"buffer_lock", "Buffer Drop Type", false, {"Off", "Hold", "Tap"}},
|
||||
{"sfx_volume", "SFX", true, "sfxSlider"},
|
||||
{"bgm_volume", "BGM", true, "bgmSlider"},
|
||||
}
|
||||
@@ -100,9 +100,10 @@ function ConfigScene:onInputPress(e)
|
||||
local option = ConfigScene.options[self.highlight]
|
||||
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[4])
|
||||
else
|
||||
playSE("cursor")
|
||||
sld = self[self.options[self.highlight][4]]
|
||||
local sld = self[self.options[self.highlight][4]]
|
||||
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 5) / (sld.max - sld.min)))
|
||||
sld:update()
|
||||
playSE("cursor")
|
||||
end
|
||||
elseif e.input == "right" or e.scancode == "right" then
|
||||
if not self.options[self.highlight][3] then
|
||||
@@ -110,9 +111,10 @@ function ConfigScene:onInputPress(e)
|
||||
local option = ConfigScene.options[self.highlight]
|
||||
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[4])
|
||||
else
|
||||
playSE("cursor")
|
||||
sld = self[self.options[self.highlight][4]]
|
||||
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))
|
||||
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))
|
||||
sld:update()
|
||||
playSE("cursor")
|
||||
end
|
||||
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||
loadSave()
|
||||
|
||||
@@ -88,7 +88,7 @@ function KeyConfigScene:onInputPress(e)
|
||||
elseif e.scancode == "tab" then
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
|
||||
self.input_state = self.input_state + 1
|
||||
elseif e.scancode ~= "escape" then
|
||||
elseif e.scancode ~= "escape" and not self.new_input[e.scancode] then
|
||||
-- all other keys can be configured
|
||||
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
|
||||
self.new_input[e.scancode] = configurable_inputs[self.input_state]
|
||||
|
||||
@@ -6,6 +6,22 @@ current_mode = 1
|
||||
current_ruleset = 1
|
||||
|
||||
function ModeSelectScene:new()
|
||||
-- reload custom modules
|
||||
initModules()
|
||||
if table.getn(game_modes) == 0 or table.getn(rulesets) == 0 then
|
||||
self.display_warning = true
|
||||
current_mode = 1
|
||||
current_ruleset = 1
|
||||
else
|
||||
self.display_warning = false
|
||||
if current_mode > table.getn(game_modes) then
|
||||
current_mode = 1
|
||||
end
|
||||
if current_ruleset > table.getn(rulesets) then
|
||||
current_ruleset = 1
|
||||
end
|
||||
end
|
||||
|
||||
self.menu_state = {
|
||||
mode = current_mode,
|
||||
ruleset = current_ruleset,
|
||||
@@ -19,6 +35,7 @@ function ModeSelectScene:new()
|
||||
rotate_180 = false,
|
||||
hold = false,
|
||||
}
|
||||
self.das = 0
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Choosing a mode",
|
||||
@@ -27,6 +44,17 @@ end
|
||||
|
||||
function ModeSelectScene:update()
|
||||
switchBGM(nil) -- experimental
|
||||
|
||||
if self.das_up or self.das_down then
|
||||
self.das = self.das + 1
|
||||
else
|
||||
self.das = 0
|
||||
end
|
||||
|
||||
if self.das >= 15 then
|
||||
self:changeOption(self.das_up and -1 or 1)
|
||||
self.das = self.das - 4
|
||||
end
|
||||
end
|
||||
|
||||
function ModeSelectScene:render()
|
||||
@@ -36,6 +64,23 @@ function ModeSelectScene:render()
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
|
||||
|
||||
if self.display_warning then
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(
|
||||
"You have no modes or rulesets.",
|
||||
80, 200, 480, "center"
|
||||
)
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.printf(
|
||||
"Come back to this menu after getting more modes or rulesets. " ..
|
||||
"Press any button to return to the main menu.",
|
||||
80, 250, 480, "center"
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
if self.menu_state.select == "mode" then
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
@@ -52,8 +97,6 @@ function ModeSelectScene:render()
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
|
||||
|
||||
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
|
||||
@@ -68,23 +111,37 @@ function ModeSelectScene:render()
|
||||
end
|
||||
|
||||
function ModeSelectScene:onInputPress(e)
|
||||
if e.input == "menu_decide" or e.scancode == "return" then
|
||||
if self.display_warning and e.input then
|
||||
scene = TitleScene()
|
||||
elseif e.type == "wheel" then
|
||||
if e.x % 2 == 1 then
|
||||
self:switchSelect()
|
||||
end
|
||||
if e.y ~= 0 then
|
||||
self:changeOption(-e.y)
|
||||
end
|
||||
elseif e.input == "menu_decide" or e.scancode == "return" then
|
||||
current_mode = self.menu_state.mode
|
||||
current_ruleset = self.menu_state.ruleset
|
||||
config.current_mode = current_mode
|
||||
config.current_ruleset = current_ruleset
|
||||
playSE("mode_decide")
|
||||
saveConfig()
|
||||
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs)
|
||||
scene = GameScene(
|
||||
game_modes[self.menu_state.mode],
|
||||
rulesets[self.menu_state.ruleset],
|
||||
self.secret_inputs
|
||||
)
|
||||
elseif e.input == "up" or e.scancode == "up" then
|
||||
self:changeOption(-1)
|
||||
playSE("cursor")
|
||||
self.das_up = true
|
||||
self.das_down = nil
|
||||
elseif e.input == "down" or e.scancode == "down" then
|
||||
self:changeOption(1)
|
||||
playSE("cursor")
|
||||
self.das_down = true
|
||||
self.das_up = nil
|
||||
elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then
|
||||
self:switchSelect()
|
||||
playSE("cursor_lr")
|
||||
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
|
||||
scene = TitleScene()
|
||||
elseif e.input then
|
||||
@@ -95,6 +152,10 @@ end
|
||||
function ModeSelectScene:onInputRelease(e)
|
||||
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then
|
||||
self.secret_inputs[e.input] = false
|
||||
elseif e.input == "up" or e.scancode == "up" then
|
||||
self.das_up = nil
|
||||
elseif e.input == "down" or e.scancode == "down" then
|
||||
self.das_down = nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -104,24 +165,26 @@ function ModeSelectScene:changeOption(rel)
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
self:changeRuleset(rel)
|
||||
end
|
||||
playSE("cursor")
|
||||
end
|
||||
|
||||
function ModeSelectScene:switchSelect(rel)
|
||||
function ModeSelectScene:switchSelect()
|
||||
if self.menu_state.select == "mode" then
|
||||
self.menu_state.select = "ruleset"
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
self.menu_state.select = "mode"
|
||||
end
|
||||
playSE("cursor_lr")
|
||||
end
|
||||
|
||||
function ModeSelectScene:changeMode(rel)
|
||||
local len = table.getn(game_modes)
|
||||
self.menu_state.mode = (self.menu_state.mode + len + rel - 1) % len + 1
|
||||
self.menu_state.mode = Mod1(self.menu_state.mode + rel, len)
|
||||
end
|
||||
|
||||
function ModeSelectScene:changeRuleset(rel)
|
||||
local len = table.getn(rulesets)
|
||||
self.menu_state.ruleset = (self.menu_state.ruleset + len + rel - 1) % len + 1
|
||||
self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len)
|
||||
end
|
||||
|
||||
return ModeSelectScene
|
||||
|
||||
@@ -104,6 +104,7 @@ function StickConfigScene:onInputPress(e)
|
||||
if not self.new_input[e.name].buttons then
|
||||
self.new_input[e.name].buttons = {}
|
||||
end
|
||||
if self.new_input[e.name].buttons[e.button] then return end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jbtn " ..
|
||||
e.button ..
|
||||
@@ -119,6 +120,9 @@ function StickConfigScene:onInputPress(e)
|
||||
if not self.new_input[e.name].axes[e.axis] then
|
||||
self.new_input[e.name].axes[e.axis] = {}
|
||||
end
|
||||
if (
|
||||
self.new_input[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"]
|
||||
) then return end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jaxis " ..
|
||||
(e.value >= 1 and "+" or "-") .. e.axis ..
|
||||
@@ -137,6 +141,9 @@ function StickConfigScene:onInputPress(e)
|
||||
if not self.new_input[e.name].hats[e.hat] then
|
||||
self.new_input[e.name].hats[e.hat] = {}
|
||||
end
|
||||
if self.new_input[e.name].hats[e.hat][e.direction] then
|
||||
return
|
||||
end
|
||||
self.set_inputs[configurable_inputs[self.input_state]] =
|
||||
"jhat " ..
|
||||
e.hat .. " " .. e.direction ..
|
||||
|
||||
@@ -349,7 +349,7 @@ function Grid:markSquares()
|
||||
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].colour = "W"
|
||||
self.grid[y+j][x+k].skin = "square"
|
||||
end
|
||||
|
||||
|
||||
@@ -118,10 +118,11 @@ function Piece:lockIfBottomed(grid)
|
||||
end
|
||||
|
||||
function Piece:addGravity(gravity, grid, classic_lock)
|
||||
gravity = gravity / (self.big and 2 or 1)
|
||||
local new_gravity = self.gravity + gravity
|
||||
if self:isDropBlocked(grid) then
|
||||
if classic_lock then
|
||||
self.gravity = math.min(1, new_gravity)
|
||||
self.gravity = new_gravity
|
||||
else
|
||||
self.gravity = 0
|
||||
self.lock_delay = self.lock_delay + 1
|
||||
|
||||
@@ -85,10 +85,11 @@ function GameMode:getDasCutDelay() return 0 end
|
||||
function GameMode:getGravity() return 1/64 end
|
||||
|
||||
function GameMode:getNextPiece(ruleset)
|
||||
local shape = self.used_randomizer:nextPiece()
|
||||
return {
|
||||
skin = self:getSkin(),
|
||||
shape = self.used_randomizer:nextPiece(),
|
||||
orientation = ruleset:getDefaultOrientation(),
|
||||
shape = shape,
|
||||
orientation = ruleset:getDefaultOrientation(shape),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -125,9 +126,6 @@ function GameMode:update(inputs, ruleset)
|
||||
if inputs["left"] or inputs["right"] then
|
||||
inputs["up"] = false
|
||||
inputs["down"] = false
|
||||
elseif inputs["up"] or inputs["down"] then
|
||||
inputs["left"] = false
|
||||
inputs["right"] = false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -156,9 +154,6 @@ function GameMode:update(inputs, ruleset)
|
||||
if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then
|
||||
self:hold(inputs, ruleset)
|
||||
self.prev_inputs = inputs
|
||||
if not self.grid:canPlacePiece(self.piece) then
|
||||
self.game_over = true
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@@ -181,7 +176,10 @@ function GameMode:update(inputs, ruleset)
|
||||
|
||||
ruleset:processPiece(
|
||||
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
|
||||
self.move, self:getLockDelay(), self:getDropSpeed(),
|
||||
(
|
||||
inputs.up and self.lock_on_hard_drop and not self.hard_drop_locked
|
||||
) and "none" or self.move,
|
||||
self:getLockDelay(), self:getDropSpeed(),
|
||||
self.drop_locked, self.hard_drop_locked,
|
||||
self.enable_hard_drop, self.additive_gravity, self.classic_lock
|
||||
)
|
||||
@@ -237,6 +235,7 @@ function GameMode:update(inputs, ruleset)
|
||||
self.lock_on_soft_drop
|
||||
then
|
||||
self.piece.locked = true
|
||||
self.piece_soft_locked = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -324,11 +323,13 @@ function GameMode:onPieceEnter() end
|
||||
function GameMode:onHold() end
|
||||
|
||||
function GameMode:onSoftDrop(dropped_row_count)
|
||||
self.drop_bonus = self.drop_bonus + 1 * dropped_row_count
|
||||
self.drop_bonus = self.drop_bonus + (
|
||||
(self.piece.big and 2 or 1) * dropped_row_count
|
||||
)
|
||||
end
|
||||
|
||||
function GameMode:onHardDrop(dropped_row_count)
|
||||
self.drop_bonus = self.drop_bonus + 2 * dropped_row_count
|
||||
self:onSoftDrop(dropped_row_count * 2)
|
||||
end
|
||||
|
||||
function GameMode:onGameOver()
|
||||
@@ -417,26 +418,38 @@ function GameMode:dasCut()
|
||||
end
|
||||
|
||||
function GameMode:areCancel(inputs, ruleset)
|
||||
if ruleset.are_cancel and self.piece_hard_dropped and
|
||||
if ruleset.are_cancel and strTrueValues(inputs) ~= "" and
|
||||
not self.prev_inputs.up and
|
||||
strTrueValues(inputs) ~= "" then
|
||||
(self.piece_hard_dropped or
|
||||
(self.piece_soft_locked and not self.prev_inputs.down)) then
|
||||
self.lcd = 0
|
||||
self.are = 0
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:checkBufferedInputs(inputs)
|
||||
if (
|
||||
config.gamesettings.buffer_lock ~= 1 and
|
||||
not self.prev_inputs["up"] and inputs["up"] and
|
||||
self.enable_hard_drop
|
||||
) then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if (
|
||||
config.gamesettings.buffer_lock ~= 1 and
|
||||
not self.prev_inputs["down"] and inputs["down"]
|
||||
) then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
if self.ready_frames == 100 then
|
||||
playedReadySE = false
|
||||
playedGoSE = false
|
||||
end
|
||||
if self.ready_frames > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
self:checkBufferedInputs(inputs)
|
||||
if not playedReadySE then
|
||||
playedReadySE = true
|
||||
playSEOnce("ready")
|
||||
@@ -450,12 +463,7 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
end
|
||||
elseif self.lcd > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
self:checkBufferedInputs(inputs)
|
||||
self.lcd = self.lcd - 1
|
||||
self:areCancel(inputs, ruleset)
|
||||
if self.lcd == 0 then
|
||||
@@ -468,12 +476,7 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
end
|
||||
end
|
||||
elseif self.are > 0 then
|
||||
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
|
||||
self.buffer_hard_drop = true
|
||||
end
|
||||
if not self.prev_inputs["down"] and inputs["down"] then
|
||||
self.buffer_soft_drop = true
|
||||
end
|
||||
self:checkBufferedInputs(inputs)
|
||||
self.are = self.are - 1
|
||||
self:areCancel(inputs, ruleset)
|
||||
if self.are == 0 then
|
||||
@@ -484,16 +487,15 @@ end
|
||||
|
||||
function GameMode:initializeOrHold(inputs, ruleset)
|
||||
if (
|
||||
(self.frames == 0 or (ruleset.are and self:getARE() ~= 0)) and self.ihs or false
|
||||
(self.frames == 0 or (ruleset.are and self:getARE() ~= 0))
|
||||
and self.ihs or false
|
||||
) and self.enable_hold and inputs["hold"] == true then
|
||||
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.game_over = true
|
||||
end
|
||||
self:onEnterOrHold(inputs, ruleset)
|
||||
end
|
||||
|
||||
function GameMode:hold(inputs, ruleset, ihs)
|
||||
@@ -506,7 +508,7 @@ function GameMode:hold(inputs, ruleset, ihs)
|
||||
self.hold_queue = {
|
||||
skin = self.piece.skin,
|
||||
shape = self.piece.shape,
|
||||
orientation = ruleset:getDefaultOrientation(),
|
||||
orientation = ruleset:getDefaultOrientation(self.piece.shape),
|
||||
}
|
||||
end
|
||||
if data == nil then
|
||||
@@ -515,67 +517,72 @@ function GameMode:hold(inputs, ruleset, ihs)
|
||||
self:initializeNextPiece(inputs, ruleset, data, false)
|
||||
end
|
||||
self.held = true
|
||||
if ihs then playSE("ihs")
|
||||
else playSE("hold") end
|
||||
self:onHold()
|
||||
if ihs then
|
||||
playSE("ihs")
|
||||
else
|
||||
playSE("hold")
|
||||
self:onEnterOrHold(inputs, ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next_piece)
|
||||
self.piece_hard_dropped = false
|
||||
local gravity = self:getGravity()
|
||||
self.piece = ruleset:initializePiece(
|
||||
inputs, piece_data, self.grid, gravity,
|
||||
self.prev_inputs, self.move,
|
||||
self:getLockDelay(), self:getDropSpeed(),
|
||||
self.lock_drop, self.lock_hard_drop, self.big_mode,
|
||||
(
|
||||
self.frames == 0 or (ruleset.are and self:getARE() ~= 0)
|
||||
) and self.irs or false
|
||||
function GameMode:onEnterOrHold(inputs, ruleset)
|
||||
if not self.grid:canPlacePiece(self.piece) then
|
||||
self.game_over = true
|
||||
return
|
||||
end
|
||||
ruleset:dropPiece(
|
||||
inputs, self.piece, self.grid, self:getGravity(),
|
||||
self:getDropSpeed(), self.drop_locked, self.hard_drop_locked
|
||||
)
|
||||
if self.buffer_hard_drop then
|
||||
if config.gamesettings.buffer_lock == 1 then
|
||||
self.piece:dropToBottom(self.grid)
|
||||
if self.lock_on_hard_drop then self.piece.locked = true end
|
||||
end
|
||||
local above_field = (
|
||||
(config.gamesettings.spawn_positions == 1 and
|
||||
ruleset.spawn_above_field) or
|
||||
config.gamesettings.spawn_positions == 3
|
||||
)
|
||||
self:onHardDrop(self.piece.position.y - (
|
||||
self.piece.big and
|
||||
ruleset.big_spawn_positions[self.piece.shape].y or
|
||||
ruleset.spawn_positions[self.piece.shape].y) +
|
||||
(above_field and ruleset:getAboveFieldOffset(
|
||||
piece_data.shape, piece_data.orientation
|
||||
) or 0)
|
||||
)
|
||||
self.buffer_hard_drop = false
|
||||
end
|
||||
if self.buffer_soft_drop then
|
||||
if (
|
||||
self.lock_on_soft_drop and
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
config.gamesettings.buffer_lock == 1
|
||||
) then
|
||||
self.piece.locked = true
|
||||
end
|
||||
self.buffer_soft_drop = false
|
||||
end
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
self.grid:canPlacePiece(self.piece) then
|
||||
playSE("bottom")
|
||||
end
|
||||
if self.lock_drop or (
|
||||
end
|
||||
|
||||
function GameMode:initializeNextPiece(
|
||||
inputs, ruleset, piece_data, generate_next_piece
|
||||
)
|
||||
if not inputs.hold and not self.buffer_soft_drop and self.lock_drop or (
|
||||
not ruleset.are or self:getARE() == 0
|
||||
) then
|
||||
self.drop_locked = true
|
||||
end
|
||||
if self.lock_hard_drop or (
|
||||
if not inputs.hold and not self.buffer_hard_drop and self.lock_hard_drop or (
|
||||
not ruleset.are or self:getARE() == 0
|
||||
) then
|
||||
self.hard_drop_locked = true
|
||||
end
|
||||
self.piece = ruleset:initializePiece(
|
||||
inputs, piece_data, self.grid, self:getGravity(),
|
||||
self.prev_inputs, self.move,
|
||||
self:getLockDelay(), self:getDropSpeed(),
|
||||
self.drop_locked, self.hard_drop_locked, self.big_mode,
|
||||
(
|
||||
self.frames == 0 or (ruleset.are and self:getARE() ~= 0)
|
||||
) and self.irs or false
|
||||
)
|
||||
if config.gamesettings.buffer_lock == 3 then
|
||||
if self.buffer_hard_drop then
|
||||
local prev_y = self.piece.position.y
|
||||
self.piece:dropToBottom(self.grid)
|
||||
self.piece.locked = self.lock_on_hard_drop
|
||||
self:onHardDrop(self.piece.position.y - prev_y)
|
||||
end
|
||||
if self.buffer_soft_drop then
|
||||
if (
|
||||
self.lock_on_soft_drop and
|
||||
self.piece:isDropBlocked(self.grid)
|
||||
) then
|
||||
self.piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
self.piece_hard_dropped = false
|
||||
self.piece_soft_locked = false
|
||||
self.buffer_hard_drop = false
|
||||
self.buffer_soft_drop = false
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
self.grid:canPlacePiece(self.piece) then
|
||||
playSE("bottom")
|
||||
end
|
||||
if generate_next_piece == nil then
|
||||
table.remove(self.next_queue, 1)
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
@@ -602,6 +609,10 @@ function GameMode:animation(x, y, skin, colour)
|
||||
}
|
||||
end
|
||||
|
||||
function GameMode:canDrawLCA()
|
||||
return self.lcd > 0
|
||||
end
|
||||
|
||||
function GameMode:drawLineClearAnimation()
|
||||
-- animation function
|
||||
-- params: block x, y, skin, colour
|
||||
@@ -678,7 +689,9 @@ function GameMode:drawPiece()
|
||||
end
|
||||
|
||||
function GameMode:drawGhostPiece(ruleset)
|
||||
if self.piece == nil then return end
|
||||
if self.piece == nil or not self.grid:canPlacePiece(self.piece) then
|
||||
return
|
||||
end
|
||||
local ghost_piece = self.piece:withOffset({x=0, y=0})
|
||||
ghost_piece.ghost = true
|
||||
ghost_piece:dropToBottom(self.grid)
|
||||
@@ -835,6 +848,104 @@ function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:drawBackground()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds[self:getBackground()],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
end
|
||||
|
||||
function GameMode:drawFrame()
|
||||
-- game frame
|
||||
if self.grid.width == 10 and self.grid.height == 24 then
|
||||
love.graphics.draw(misc_graphics["frame"], 48, 64)
|
||||
else
|
||||
love.graphics.setColor(174/255, 83/255, 76/255, 1)
|
||||
love.graphics.setLineWidth(8)
|
||||
love.graphics.line(
|
||||
60,76,
|
||||
68+16*self.grid.width,76,
|
||||
68+16*self.grid.width,84+16*(self.grid.height-4),
|
||||
60,84+16*(self.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.grid.width,76,
|
||||
68+16*self.grid.width,84+16*(self.grid.height-4),
|
||||
60,84+16*(self.grid.height-4),
|
||||
60,76
|
||||
)
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setColor(0, 0, 0, 200)
|
||||
love.graphics.rectangle(
|
||||
"fill", 64, 80,
|
||||
16 * self.grid.width, 16 * (self.grid.height - 4)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:drawReadyGo()
|
||||
-- ready/go graphics
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
if self.ready_frames <= 100 and self.ready_frames > 52 then
|
||||
love.graphics.draw(misc_graphics["ready"], 144 - 50, 240 - 14)
|
||||
elseif self.ready_frames <= 50 and self.ready_frames > 2 then
|
||||
love.graphics.draw(misc_graphics["go"], 144 - 27, 240 - 14)
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:drawCustom() end
|
||||
|
||||
function GameMode:drawIfPaused()
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf("GAME PAUSED!", 64, 160, 160, "center")
|
||||
end
|
||||
|
||||
-- transforms specified in here will transform the whole screen
|
||||
-- if you want a transform for a particular component, push the
|
||||
-- default transform by using love.graphics.push(), do your
|
||||
-- transform, and then love.graphics.pop() at the end of that
|
||||
-- component's draw call!
|
||||
function GameMode:transformScreen() end
|
||||
|
||||
function GameMode:draw(paused)
|
||||
self:transformScreen()
|
||||
self:drawBackground()
|
||||
self:drawFrame()
|
||||
self:drawGrid()
|
||||
self:drawPiece()
|
||||
self:drawNextQueue(self.ruleset)
|
||||
self:drawScoringInfo()
|
||||
self:drawReadyGo()
|
||||
self:drawCustom()
|
||||
if self:canDrawLCA() then
|
||||
self:drawLineClearAnimation()
|
||||
end
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
if config.gamesettings.display_gamemode == 1 then
|
||||
love.graphics.printf(
|
||||
self.name .. " - " .. self.ruleset.name,
|
||||
0, 460, 640, "left"
|
||||
)
|
||||
end
|
||||
|
||||
if paused then
|
||||
self:drawIfPaused()
|
||||
end
|
||||
|
||||
if self.completed then
|
||||
self:onGameComplete()
|
||||
elseif self.game_over then
|
||||
self:onGameOver()
|
||||
end
|
||||
end
|
||||
|
||||
return GameMode
|
||||
|
||||
@@ -144,7 +144,7 @@ function Marathon2020Game:advanceOneFrame()
|
||||
if self.roll_frames < 0 then
|
||||
return false
|
||||
elseif self.roll_frames > 4000 then
|
||||
if self.grade >= 30 and self.section_cool_count >= 20 then self.grade = 31 end
|
||||
if self:qualifiesForMRoll() then self.grade = 31 end
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
@@ -154,11 +154,11 @@ function Marathon2020Game:advanceOneFrame()
|
||||
end
|
||||
|
||||
local cool_cutoffs = {
|
||||
frameTime(0,45,00), frameTime(0,41,50), frameTime(0,38,50), frameTime(0,35,00), frameTime(0,32,50),
|
||||
frameTime(0,29,20), frameTime(0,27,20), frameTime(0,24,80), frameTime(0,22,80), frameTime(0,20,60),
|
||||
frameTime(0,19,60), frameTime(0,19,40), frameTime(0,19,40), frameTime(0,18,40), frameTime(0,18,20),
|
||||
frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20),
|
||||
frameTime(0,15,20)
|
||||
[0] = frameTime(0,45,00),
|
||||
frameTime(0,41,50), frameTime(0,38,50), frameTime(0,35,00), frameTime(0,32,50), frameTime(0,29,20),
|
||||
frameTime(0,27,20), frameTime(0,24,80), frameTime(0,22,80), frameTime(0,20,60), frameTime(0,19,60),
|
||||
frameTime(0,19,40), frameTime(0,19,40), frameTime(0,18,40), frameTime(0,18,20), frameTime(0,16,20),
|
||||
frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,15,20)
|
||||
}
|
||||
|
||||
local levels_for_cleared_rows = { 1, 2, 4, 6 }
|
||||
@@ -227,13 +227,14 @@ local mid_cleared_line_points = {2, 6, 12, 24}
|
||||
local high_cleared_line_points = {1, 4, 9, 20}
|
||||
|
||||
local function getGradeForGradePoints(points)
|
||||
return math.floor(math.sqrt((points / 50) * 8 + 1) / 2 - 0.5)
|
||||
return math.min(30, math.floor(math.sqrt((points / 50) * 8 + 1) / 2 - 0.5))
|
||||
-- Don't be afraid of the above function. All it does is make it so that
|
||||
-- you need 50 points to get to grade 1, 100 points to grade 2, etc.
|
||||
end
|
||||
|
||||
function Marathon2020Game:updateGrade(cleared_lines)
|
||||
-- update grade points and max grade points
|
||||
if self.clear then return end
|
||||
local point_level = math.floor(self.level / 100) + self.delay_level
|
||||
local plus_points = math.max(
|
||||
low_cleared_line_points[cleared_lines],
|
||||
@@ -249,12 +250,11 @@ function Marathon2020Game:updateGrade(cleared_lines)
|
||||
end
|
||||
|
||||
function Marathon2020Game:getTotalGrade()
|
||||
if self.grade + self.section_cool_count > 50 then return "GM" end
|
||||
return self.grade + self.section_cool_count
|
||||
end
|
||||
|
||||
local function getSectionForLevel(level)
|
||||
if level < 2001 then
|
||||
if level < 2000 then
|
||||
return math.floor(level / 100) + 1
|
||||
elseif level < 2020 then
|
||||
return 20
|
||||
@@ -290,10 +290,10 @@ function Marathon2020Game:sectionPassed(old_level, new_level)
|
||||
end
|
||||
|
||||
function Marathon2020Game:checkTorikan(section)
|
||||
if section == 5 and self.frames < frameTime(6,00,00) then self.torikan_passed[500] = true end
|
||||
if section == 9 and self.frames < frameTime(8,30,00) then self.torikan_passed[900] = true end
|
||||
if section == 10 and self.frames < frameTime(8,45,00) then self.torikan_passed[1000] = true end
|
||||
if section == 15 and self.frames < frameTime(11,30,00) then self.torikan_passed[1500] = true end
|
||||
if section == 5 and self.frames < frameTime(8,00,00) then self.torikan_passed[500] = true end
|
||||
if section == 9 and self.frames < frameTime(10,30,00) then self.torikan_passed[900] = true end
|
||||
if section == 10 and self.frames < frameTime(10,45,00) then self.torikan_passed[1000] = true end
|
||||
if section == 15 and self.frames < frameTime(12,30,00) then self.torikan_passed[1500] = true end
|
||||
if section == 19 and self.frames < frameTime(13,15,00) then self.torikan_passed[1900] = true end
|
||||
end
|
||||
|
||||
@@ -331,14 +331,16 @@ end
|
||||
function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
function sectionCool(section)
|
||||
self.section_cool_count = self.section_cool_count + 1
|
||||
if section <= 10 then
|
||||
self.delay_level = math.min(20, self.delay_level + 1)
|
||||
if section < 10 then table.insert(self.section_status, "cool") end
|
||||
end
|
||||
table.insert(self.section_status, "cool")
|
||||
self.cool_timer = 300
|
||||
end
|
||||
|
||||
local section = getSectionForLevel(old_level)
|
||||
|
||||
if section <= 19 and old_level % 100 < 70 and new_level >= math.floor(old_level / 100) * 100 + 70 then
|
||||
if old_level % 100 < 70 and new_level >= math.floor(old_level / 100) * 100 + 70 then
|
||||
-- record section 70 time
|
||||
section_70_time = self.frames - self.section_start_time
|
||||
table.insert(self.secondary_section_times, section_70_time)
|
||||
@@ -350,23 +352,25 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
|
||||
if section > 5 then self.delay_level = math.min(20, self.delay_level + 1) end
|
||||
self:checkTorikan(section)
|
||||
self:checkClear(new_level)
|
||||
|
||||
if (
|
||||
section <= 19 and self.section_status[section - 1] == "cool" and
|
||||
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
|
||||
self.secondary_section_times[section] < cool_cutoffs[section]
|
||||
self.section_status[section - 1] == "cool" and
|
||||
self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120 and
|
||||
self.secondary_section_times[section] < cool_cutoffs[self.delay_level]
|
||||
) then
|
||||
sectionCool(section)
|
||||
elseif self.section_status[section - 1] == "cool" then
|
||||
table.insert(self.section_status, "none")
|
||||
elseif section <= 19 and self.secondary_section_times[section] < cool_cutoffs[section] then
|
||||
elseif self.secondary_section_times[section] < cool_cutoffs[self.delay_level] then
|
||||
sectionCool(section)
|
||||
else
|
||||
table.insert(self.section_status, "none")
|
||||
end
|
||||
|
||||
if section > 5 then
|
||||
self.delay_level = math.min(20, self.delay_level + 1)
|
||||
end
|
||||
self:checkTorikan(section)
|
||||
self:checkClear(new_level)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -403,11 +407,12 @@ GM-roll requirements
|
||||
You qualify for the GM roll if you:
|
||||
- Reach level 2020
|
||||
- with a grade of 50
|
||||
- and at least 25,000 grade points
|
||||
- in less than 13:30.00 total.
|
||||
|
||||
]]--
|
||||
|
||||
return self.level >= 2020 and self:getTotalGrade() == 50 and self.frames <= frameTime(13,30)
|
||||
return self.level >= 2020 and self:getTotalGrade() == 50 and self.grade_points >= 25000 and self.frames <= frameTime(13,30)
|
||||
end
|
||||
|
||||
function Marathon2020Game:drawGrid()
|
||||
@@ -452,7 +457,13 @@ function Marathon2020Game:drawScoringInfo()
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
|
||||
|
||||
local grade = self:getTotalGrade()
|
||||
love.graphics.printf(
|
||||
grade > 50 and "GM" or grade,
|
||||
text_x, 120, 90, "left"
|
||||
)
|
||||
|
||||
love.graphics.printf(self.grade_points, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
||||
|
||||
@@ -466,7 +477,7 @@ end
|
||||
|
||||
function Marathon2020Game:getHighscoreData()
|
||||
return {
|
||||
grade = self.grade,
|
||||
grade = self:getTotalGrade(),
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ function MarathonA2Game:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.grade_combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
self.grade = 0
|
||||
self.grade_points = 0
|
||||
@@ -135,15 +136,23 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if cleared_lines >= 4 then
|
||||
self.tetris_count = self.tetris_count + 1
|
||||
end
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if self.grid:checkForBravo(cleared_lines) then
|
||||
self.bravo = 4
|
||||
else
|
||||
self.bravo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
if cleared_lines > 1 then
|
||||
self.grade_combo = self.grade_combo + 1
|
||||
end
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
self.grade_combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
else self.lines = self.lines + cleared_lines end
|
||||
@@ -253,7 +262,7 @@ function MarathonA2Game:updateGrade(cleared_lines)
|
||||
self.grade_points = self.grade_points + (
|
||||
math.ceil(
|
||||
grade_point_bonuses[self.grade + 1][cleared_lines] *
|
||||
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
|
||||
combo_multipliers[math.min(self.grade_combo, 10)][cleared_lines]
|
||||
) * (1 + math.floor(self.level / 250))
|
||||
)
|
||||
if self.grade_points >= 100 and self.grade < 31 then
|
||||
|
||||
@@ -20,6 +20,7 @@ function MarathonA3Game:new()
|
||||
self.speed_level = 0
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.grade_combo = 1
|
||||
self.grade = 0
|
||||
self.grade_points = 0
|
||||
self.roll_points = 0
|
||||
@@ -217,13 +218,8 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
||||
section_70_time = self.frames - self.section_start_time
|
||||
table.insert(self.secondary_section_times, section_70_time)
|
||||
|
||||
if section <= 9 and self.section_status[section - 1] == "cool" and
|
||||
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 then
|
||||
self.section_cool = true
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 300
|
||||
elseif self.section_status[section - 1] == "cool" then self.section_cool = false
|
||||
elseif section <= 9 and self.secondary_section_times[section] < cool_cutoffs[section] then
|
||||
if section <= 9 and self.secondary_section_times[section] < cool_cutoffs[section] and
|
||||
(section == 1 or self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120) then
|
||||
self.section_cool = true
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 300
|
||||
@@ -236,12 +232,16 @@ function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
if cleared_lines > 1 then
|
||||
self.grade_combo = self.grade_combo + 1
|
||||
end
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
self.grade_combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
@@ -335,7 +335,7 @@ function MarathonA3Game:updateGrade(cleared_lines)
|
||||
self.grade_points = self.grade_points + (
|
||||
math.ceil(
|
||||
grade_point_bonuses[self.grade + 1][cleared_lines] *
|
||||
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
|
||||
combo_multipliers[math.min(self.grade_combo, 10)][cleared_lines]
|
||||
) * (1 + math.floor(self.level / 250))
|
||||
)
|
||||
if self.grade_points >= 100 and self.grade < 31 then
|
||||
@@ -351,7 +351,12 @@ function MarathonA3Game:qualifiesForMRoll()
|
||||
end
|
||||
|
||||
function MarathonA3Game:getAggregateGrade()
|
||||
return self.section_cool_grade + math.floor(self.roll_points / 100) + grade_conversion[self.grade]
|
||||
return math.min(
|
||||
self.section_cool_grade +
|
||||
math.floor(self.roll_points / 100) +
|
||||
grade_conversion[self.grade],
|
||||
self.roll_frames > 3238 and 32 or 31
|
||||
)
|
||||
end
|
||||
|
||||
local master_grades = { "M", "MK", "MV", "MO", "MM" }
|
||||
@@ -366,8 +371,6 @@ function MarathonA3Game:getLetterGrade()
|
||||
return "M" .. tostring(grade - 17)
|
||||
elseif grade < 32 then
|
||||
return master_grades[grade - 26]
|
||||
elseif grade >= 32 and self.roll_frames < 3238 then
|
||||
return "MM"
|
||||
else
|
||||
return "GM"
|
||||
end
|
||||
@@ -465,7 +468,7 @@ function MarathonA3Game:drawScoringInfo()
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.level >= 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
elseif self.level >= 999 then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
|
||||
@@ -19,13 +19,16 @@ function PhantomManiaGame:new()
|
||||
self.next_queue_length = 1
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.tetrises = 0
|
||||
self.section_tetrises = {[0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
self.section_req = true
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
end
|
||||
|
||||
@@ -38,7 +41,7 @@ function PhantomManiaGame:getARE()
|
||||
end
|
||||
|
||||
function PhantomManiaGame:getLineARE()
|
||||
if self.level < 100 then return 18
|
||||
if self.level < 100 then return 14
|
||||
elseif self.level < 400 then return 8
|
||||
elseif self.level < 500 then return 7
|
||||
else return 6 end
|
||||
@@ -105,12 +108,23 @@ end
|
||||
|
||||
function PhantomManiaGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
if cleared_row_count >= 4 then
|
||||
self.tetrises = self.tetrises + 1
|
||||
self.section_tetrises[math.floor(self.level / 100)] = (
|
||||
self.section_tetrises[math.floor(self.level / 100)] + 1
|
||||
)
|
||||
end
|
||||
local new_level = self.level + cleared_row_count
|
||||
if new_level >= 999 or self:hitTorikan(self.level, new_level) then
|
||||
if new_level >= 999 then
|
||||
self.level = 999
|
||||
end
|
||||
self.clear = true
|
||||
for i = 0, 9 do
|
||||
if self.section_tetrises[i] < (i == 9 and 1 or 2) then
|
||||
self.section_req = false
|
||||
end
|
||||
end
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
@@ -138,7 +152,7 @@ PhantomManiaGame.rollOpacityFunction = function(age)
|
||||
end
|
||||
|
||||
function PhantomManiaGame:drawGrid()
|
||||
if not (self.game_over or self.clear) then
|
||||
if not (self.game_over or self.completed or (self.clear and self.level < 999)) then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
|
||||
else
|
||||
self.grid:draw()
|
||||
@@ -150,16 +164,14 @@ local function getLetterGrade(level, clear)
|
||||
return ""
|
||||
elseif level < 500 or level == 500 and clear then
|
||||
return "M"
|
||||
elseif level < 700 then
|
||||
elseif level < 600 then
|
||||
return "MK"
|
||||
elseif level < 800 or level == 800 and clear then
|
||||
elseif level < 700 then
|
||||
return "MV"
|
||||
elseif level < 900 then
|
||||
elseif level < 800 or level == 800 and clear then
|
||||
return "MO"
|
||||
elseif level < 999 then
|
||||
elseif level <= 999 then
|
||||
return "MM"
|
||||
elseif level == 999 then
|
||||
return "GM"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -169,7 +181,9 @@ function PhantomManiaGame:drawScoringInfo()
|
||||
local text_x = config["side_next"] and 320 or 240
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
end
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
@@ -178,7 +192,16 @@ function PhantomManiaGame:drawScoringInfo()
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then
|
||||
if self.roll_frames > 1982 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.level == 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
if self.level == 999 and self.section_req and self.tetrises >= 31 then
|
||||
love.graphics.printf("GM", text_x, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
|
||||
end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
if self.clear then
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'funcs'
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
|
||||
|
||||
local PhantomMania2Game = GameMode:extend()
|
||||
|
||||
@@ -28,7 +28,7 @@ function PhantomMania2Game:new()
|
||||
|
||||
self.SGnames = {
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
|
||||
end
|
||||
|
||||
function PhantomMania2Game:onHold()
|
||||
self.super.onHold()
|
||||
self.super:onHold()
|
||||
self.hold_age = 0
|
||||
end
|
||||
|
||||
@@ -251,7 +251,7 @@ PhantomMania2Game.garbageOpacityFunction = function(age)
|
||||
end
|
||||
|
||||
function PhantomMania2Game:drawGrid()
|
||||
if not (self.game_over) then
|
||||
if not (self.game_over or self.completed or (self.clear and self.level < 1300)) then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
||||
else
|
||||
self.grid:draw()
|
||||
@@ -287,7 +287,8 @@ function PhantomMania2Game:setHoldOpacity()
|
||||
if self.level > 1000 and self.level < 1300 then
|
||||
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.hold_age / 15))
|
||||
else
|
||||
self.super:setHoldOpacity(1, self.held and 0.6 or 1)
|
||||
local colour = self.held and 0.6 or 1
|
||||
love.graphics.setColor(colour, colour, colour, 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,12 +9,6 @@ PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS su
|
||||
function PhantomManiaNGame:new()
|
||||
PhantomManiaNGame.super:new()
|
||||
|
||||
self.SGnames = {
|
||||
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||
"M10", "M11", "M12", "M13", "M14", "M15", "M16", "M17", "M18",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.next_queue_length = 3
|
||||
self.enable_hold = true
|
||||
end
|
||||
|
||||
@@ -1,217 +1,18 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local MarathonA1Game = require 'tetris.modes.marathon_a1'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'
|
||||
|
||||
local SurvivalA1Game = GameMode:extend()
|
||||
local SurvivalA1Game = MarathonA1Game:extend()
|
||||
|
||||
SurvivalA1Game.name = "Survival A1"
|
||||
SurvivalA1Game.hash = "SurvivalA1"
|
||||
SurvivalA1Game.tagline = "The game starts fast and only gets faster!"
|
||||
|
||||
|
||||
|
||||
|
||||
function SurvivalA1Game:new()
|
||||
SurvivalA1Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.bravos = 0
|
||||
|
||||
self.gm_conditions = {
|
||||
level300 = false,
|
||||
level500 = false,
|
||||
level999 = false
|
||||
}
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History4RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.enable_hard_drop = false
|
||||
self.enable_hold = false
|
||||
self.next_queue_length = 1
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getARE()
|
||||
return 30
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getLineARE()
|
||||
return 27
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getDasLimit()
|
||||
return 15
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getLineClearDelay()
|
||||
return 44
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getLockDelay()
|
||||
return 30
|
||||
end
|
||||
SurvivalA1Game.tagline = "A constant high-speed marathon!"
|
||||
|
||||
function SurvivalA1Game:getGravity()
|
||||
return 20
|
||||
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 = "???"}
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames > 2968 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SurvivalA1Game:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:onLineClear(cleared_row_count)
|
||||
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if new_level == 999 then
|
||||
self.clear = true
|
||||
end
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if self.grid:checkForBravo(cleared_lines) then
|
||||
self.bravo = 4
|
||||
self.bravos = self.bravos + 1
|
||||
else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:checkGMRequirements(old_level, new_level)
|
||||
if old_level < 300 and new_level >= 300 then
|
||||
if self.score >= 12000 and self.frames <= frameTime(4,15) then
|
||||
self.gm_conditions["level300"] = true
|
||||
end
|
||||
elseif old_level < 500 and new_level >= 500 then
|
||||
if self.score >= 40000 and self.frames <= frameTime(7,30) then
|
||||
self.gm_conditions["level500"] = true
|
||||
end
|
||||
elseif old_level < 999 and new_level >= 999 then
|
||||
if self.score >= 126000 and self.frames <= frameTime(13,30) then
|
||||
self.gm_conditions["level999"] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:drawGrid()
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
function SurvivalA1Game:drawScoringInfo()
|
||||
SurvivalA1Game.super.drawScoringInfo(self)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||
end
|
||||
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getSectionEndLevel()
|
||||
if self.level >= 900 then return 999
|
||||
else return math.floor(self.level / 100 + 1) * 100 end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
function SurvivalA1Game:getHighscoreData()
|
||||
return {
|
||||
grade = self.grade,
|
||||
score = self.score,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
return SurvivalA1Game
|
||||
|
||||
@@ -79,7 +79,7 @@ end
|
||||
function SurvivalA2Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames > 2968 then
|
||||
if self.roll_frames > 1800 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
@@ -97,14 +97,13 @@ end
|
||||
function SurvivalA2Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 or self:hitTorikan(self.level, new_level) then
|
||||
if new_level == 999 or self:hitTorikan(self.level, new_level) then
|
||||
self.clear = true
|
||||
if self.level < 999 then
|
||||
if new_level < 999 then
|
||||
self.game_over = true
|
||||
end
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
|
||||
@@ -158,7 +157,7 @@ function SurvivalA2Game:drawScoringInfo()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
if self.roll_frames > 1800 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.level >= 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -24,7 +24,7 @@ function SurvivalA3Game:new()
|
||||
|
||||
self.SGnames = {
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
@@ -38,11 +38,9 @@ function SurvivalA3Game:new()
|
||||
end
|
||||
|
||||
function SurvivalA3Game:initialize(ruleset)
|
||||
|
||||
self.torikan_time = frameTime(2,28)
|
||||
if ruleset.world then self.torikan_time = frameTime(3,03) end
|
||||
self.super.initialize(self, ruleset)
|
||||
-- ^ notice the . here instead of the :
|
||||
GameMode.initialize(self, ruleset)
|
||||
end
|
||||
|
||||
function SurvivalA3Game:getARE()
|
||||
@@ -239,7 +237,7 @@ function SurvivalA3Game:drawScoringInfo()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.level >= 1300 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
elseif self.level >= 1300 then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
|
||||
@@ -375,7 +375,10 @@ end
|
||||
|
||||
function CRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
if piece.shape == "O" then return end
|
||||
if piece.shape == "O" then
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
|
||||
local kicks = CRS.wallkicks[piece.shape][piece:isDropBlocked(grid)][piece.rotation][new_piece.rotation]
|
||||
|
||||
|
||||
@@ -47,18 +47,11 @@ function Ruleset:new(game_mode)
|
||||
else
|
||||
bones = config.gamesettings.piece_colour == 3 and "w" or ""
|
||||
end
|
||||
blocks.bone = {
|
||||
R = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
O = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
Y = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
G = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
C = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
B = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
M = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
F = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
A = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
X = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
|
||||
}
|
||||
for colour in pairs(blocks["2tie"]) do
|
||||
blocks.bone[colour] = love.graphics.newImage(
|
||||
"res/img/bone" .. bones .. ".png"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
@@ -76,7 +69,7 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
self:attemptRotate(new_inputs, piece, grid, initial)
|
||||
end
|
||||
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
if not initial and not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
|
||||
@@ -161,24 +154,19 @@ function Ruleset:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
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
|
||||
piece:addGravity(gravity + drop_speed, grid, classic_lock)
|
||||
else
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
|
||||
end
|
||||
elseif inputs["up"] == true and hard_drop_enabled == true then
|
||||
if inputs["up"] == true and hard_drop_enabled == true then
|
||||
if hard_drop_locked == true or piece:isDropBlocked(grid) then
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
else
|
||||
piece:dropToBottom(grid)
|
||||
end
|
||||
elseif inputs["down"] == true and drop_locked == false then
|
||||
if additive_gravity then
|
||||
piece:addGravity(gravity + drop_speed, grid, classic_lock)
|
||||
else
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
|
||||
end
|
||||
else
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
end
|
||||
@@ -226,16 +214,7 @@ function Ruleset:initializePiece(
|
||||
colours = self.colourscheme
|
||||
end
|
||||
|
||||
local spawn_x
|
||||
if (grid.width ~= 10) then
|
||||
local percent = spawn_positions[data.shape].x / 10
|
||||
for i = grid.width - 1, 0, -1 do
|
||||
if i / grid.width <= percent then
|
||||
spawn_x = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local spawn_x = math.floor(spawn_positions[data.shape].x * grid.width / 10)
|
||||
|
||||
local spawn_dy
|
||||
if (config.gamesettings.spawn_positions == 1) then
|
||||
@@ -251,7 +230,7 @@ function Ruleset:initializePiece(
|
||||
end
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_x or spawn_positions[data.shape].x,
|
||||
x = spawn_x,
|
||||
y = spawn_positions[data.shape].y - spawn_dy
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
@@ -262,7 +241,6 @@ function Ruleset:initializePiece(
|
||||
playSE("irs")
|
||||
end
|
||||
end
|
||||
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
|
||||
return piece
|
||||
end
|
||||
|
||||
@@ -275,15 +253,13 @@ function Ruleset:processPiece(
|
||||
drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
if piece.locked then return end
|
||||
|
||||
local synchroes_allowed = ({not self.world, true, false})[config.gamesettings.synchroes_allowed]
|
||||
|
||||
if synchroes_allowed then
|
||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false)
|
||||
self:movePiece(piece, grid, move, gravity >= 20)
|
||||
self:movePiece(piece, grid, move, gravity >= grid.height - 4)
|
||||
else
|
||||
self:movePiece(piece, grid, move, gravity >= 20)
|
||||
self:movePiece(piece, grid, move, gravity >= grid.height - 4)
|
||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false)
|
||||
end
|
||||
self:dropPiece(
|
||||
|
||||
Reference in New Issue
Block a user