Merge pull request #1 from SashLilac/master

Update fork
This commit is contained in:
Mizu 2020-11-11 17:17:22 +01:00 committed by GitHub
commit e24737a3b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 1686 additions and 3519 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
*.love *.love
dist/*.zip dist/*.zip
dist/**/cambridge.exe dist/**/cambridge.exe
dist/**/libs
dist/**/*.md

View File

@ -5,48 +5,55 @@ Cambridge
Welcome to Cambridge, the next open-source falling-block game engine! Welcome to Cambridge, the next open-source falling-block game engine!
This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac) and [Oshisaure](https://github.com/oshisaure)! This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)!
Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4 Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4
Credits Credits
------- -------
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for their amazing contributions to my life in general! - [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 Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
- [joezeng](https://github.com/joezeng) for the original project. - [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
- [Hailey](https://github.com/haileylgbt) for some miscellaneous assets.
- CylinderKnot for an amazing gamemode. The following people in no particular order also helped with the project:
- MarkGamed7794 for some miscellaneous contributions. - [Hailey](https://github.com/haileylgbt)
- Mizu for the Cambridge logo and the [Cambridge launcher](https://github.com/rexxt/cambridge-launcher). - CylinderKnot
- MattMayuga for the Cambridge banner. - MarkGamed7794
- [Mizu](https://github.com/rexxt)
- MattMayuga
- Kitaru
- switchpalacecorner
- [sinefuse](https://github.com/sinefuse)
- [2Tie](https://github.com/2Tie)
- [nightmareci](https://github.com/nightmareci)
- [MyPasswordIsWeak](https://github.com/MyPasswordIsWeak)
![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png) ![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png)
Installation instructions Playing the game
------------------------- ----------------
Pre-built releases are available on the releases page.
### Windows ### Windows
Unzip the exe file and run it directly. All assets are currently bundled inside the executable. You do not need LÖVE on Windows, as it comes bundled with the program.
### macOS To get the stable release, simply download the ZIP in the latest release. All assets needed are bundled with the executable.
For the time being, the file `cambridge.love` only works on the command line. Install `love` with [Homebrew](https://brew.sh), and run: If you want the bleeding edge version, or want mod pack support, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
$ love cambridge.love Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
### Linux dist\windows\love.exe .
Same as macOS, except install `love` with your favourite package manager. Alternatively, if you're on a 32-bit system, run this instead:
dist\win32\love.exe .
Running from source 32-bit systems do not support rich presence integration.
-------------------
If you want the bleeding-edge release, you can also clone the code straight from this repository. Then, check the mod pack section at the bottom of this page.
### macOS, Linux ### macOS, Linux
@ -54,14 +61,21 @@ If you haven't already, install `love` with your favourite package manager (Home
Clone the repository in git: Clone the repository in git:
git clone https://github.com/SashLilac/cambridge git clone https://github.com/SashLilac/cambridge
Alternatively, download the source code ZIP in the latest release.
Then, navigate to the root directory that you just cloned, and type: Then, navigate to the root directory that you just cloned, and type:
love . love .
It should run automatically! It should run automatically!
## Installing modpacks
Simply drag your mode, ruleset, and randomizer Lua files into their respective directory, and they should appear automatically.
Alternatively, install [this](https://github.com/SashLilac/cambridge/releases/download/v0.2.1/modpackv1.zip) mod pack to get a taste of the mod potential.
License License
------- -------

11
clean.bat Normal file
View File

@ -0,0 +1,11 @@
@del cambridge.love
@del dist\windows\cambridge.exe
@del dist\windows\SOURCES.md
@del dist\windows\LICENSE.md
@rmdir /Q /S dist\windows\libs
@del dist\win32\cambridge.exe
@del dist\win32\SOURCES.md
@del dist\win32\LICENSE.md
@rmdir /Q /S dist\win32\libs
@del dist\cambridge-windows.zip
@del dist\cambridge-win32.zip

View File

@ -4,17 +4,17 @@ Game modes
There are several classes of game modes. The modes that originate from other games are organized by suffix: There are several classes of game modes. The modes that originate from other games are organized by suffix:
* The "C" series stand for "Classic" games, games that were produced before around 1992-1993 and generally have no wallkicks or lock delay. * The "C" series stand for "Classic" games, games that were produced before around 1992-1993 and generally have no wallkicks or lock delay.
* C84 - The original version from the Electronika 60. * C84 - The original version from the Electronika 60.
* C88 - Sega Tetris. * C88 - Sega Tetris.
* C89 - Nintendo / NES Tetris. * C89 - Nintendo / NES Tetris.
* The "A" series stand for "Arika" games, or games in the Tetris the Grand Master series. * The "A" series stand for "Arika" games, or games in the Tetris the Grand Master series.
* A1 - Tetris The Grand Master (the original from 1998). * A1 - Tetris The Grand Master (the original from 1998).
* A2 - Tetris The Absolute The Grand Master 2 PLUS. * A2 - Tetris The Absolute The Grand Master 2 PLUS.
* A3 - Tetris The Grand Master 3 Terror-Instinct. * A3 - Tetris The Grand Master 3 Terror-Instinct.
* AX - Tetris The Grand Master ACE (X for Xbox). * AX - Tetris The Grand Master ACE (X for Xbox).
* The "G" series stand for "Guideline" games, or games that follow the Tetris Guideline. * The "G" series stand for "Guideline" games, or games that follow the Tetris Guideline.
* GF - Tetris Friends (2007-2019) * GF - Tetris Friends (2007-2019)
* GJ - Tetris Online Japan (2005-2011) * GJ - Tetris Online Japan (2005-2011)
* N stands for Nullpomino, only used for Phantom Mania N. * N stands for Nullpomino, only used for Phantom Mania N.
MARATHON MARATHON

View File

@ -1,11 +1,11 @@
function copy(t) function copy(t)
-- returns deep copy of t (as opposed to the shallow copy you get from var = t) -- returns deep copy of t (as opposed to the shallow copy you get from var = t)
if type(t) ~= "table" then return t end if type(t) ~= "table" then return t end
local meta = getmetatable(t) local meta = getmetatable(t)
local target = {} local target = {}
for k, v in pairs(t) do target[k] = v end for k, v in pairs(t) do target[k] = v end
setmetatable(target, meta) setmetatable(target, meta)
return target return target
end end
function strTrueValues(tbl) function strTrueValues(tbl)
@ -64,6 +64,6 @@ function formatBigNum(number)
end end
function Mod1(n, m) function Mod1(n, m)
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1]) -- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
return ((n-1) % m) + 1 return ((n-1) % m) + 1
end end

View File

@ -5,12 +5,26 @@ local ffi = require "ffi"
local osname = love.system.getOS() local osname = love.system.getOS()
local discordRPClib = nil local discordRPClib = nil
-- FFI requires the libraries really be files just sitting in the filesystem. It
-- can't load libraries from a .love archive, nor a fused executable on Windows.
-- Merely using love.filesystem.getSource() only works when running LOVE with
-- the game unarchived from command line, like "love .".
--
-- The code here setting "source" will set the directory where the game was run
-- from, so FFI can load discordRPC. We assume that the discordRPC library's
-- libs directory is in the same directory as the .love archive; if it's
-- missing, it just won't load.
local source = love.filesystem.getSource()
if string.sub(source, -5) == ".love" or love.filesystem.isFused() then
source = love.filesystem.getSourceBaseDirectory()
end
if osname == "Linux" then if osname == "Linux" then
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.so") discordRPClib = ffi.load(source.."/libs/discord-rpc.so")
elseif osname == "OS X" then elseif osname == "OS X" then
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.dylib") discordRPClib = ffi.load(source.."/libs/discord-rpc.dylib")
elseif osname == "Windows" then elseif osname == "Windows" then
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.dll") discordRPClib = ffi.load(source.."/libs/discord-rpc.dll")
else else
-- Else it crashes later on -- Else it crashes later on
error(string.format("Discord rpc not supported on platform (%s)", osname)) error(string.format("Discord rpc not supported on platform (%s)", osname))

View File

@ -20,8 +20,8 @@ backgrounds = {
love.graphics.newImage("res/backgrounds/1800-railways.png"), love.graphics.newImage("res/backgrounds/1800-railways.png"),
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"), love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"), title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"), input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"),
game_config = love.graphics.newImage("res/backgrounds/options-gears.png"), game_config = love.graphics.newImage("res/backgrounds/options-gears.png"),
} }
blocks = { blocks = {
@ -33,6 +33,8 @@ blocks = {
C = love.graphics.newImage("res/img/s2.png"), C = love.graphics.newImage("res/img/s2.png"),
B = love.graphics.newImage("res/img/s4.png"), B = love.graphics.newImage("res/img/s4.png"),
M = love.graphics.newImage("res/img/s5.png"), M = love.graphics.newImage("res/img/s5.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"), X = love.graphics.newImage("res/img/s9.png"),
}, },
["bone"] = { ["bone"] = {
@ -43,29 +45,31 @@ blocks = {
C = love.graphics.newImage("res/img/bone.png"), C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"), B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"), M = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"), X = love.graphics.newImage("res/img/bone.png"),
} }
} }
ColourSchemes = { ColourSchemes = {
Arika = { Arika = {
I = "R", I = "R",
L = "O", L = "O",
J = "B", J = "B",
S = "M", S = "M",
Z = "G", Z = "G",
O = "Y", O = "Y",
T = "C", T = "C",
}, },
TTC = { TTC = {
I = "C", I = "C",
L = "O", L = "O",
J = "B", J = "B",
S = "G", S = "G",
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
}, },
} }
for name, blockset in pairs(blocks) do for name, blockset in pairs(blocks) do

View File

@ -1,58 +1,58 @@
print("Loading discord RPC...") print("Loading discord RPC...")
DiscordRPC = { DiscordRPC = {
loaded = false loaded = false
} }
local success, RPC = pcall(require, "libs.discordRPC") local success, RPC = pcall(require, "libs.discordRPC")
if success then if success then
DiscordRPC.loaded = true DiscordRPC.loaded = true
DiscordRPC.appId = "599778517789573120" DiscordRPC.appId = "599778517789573120"
function RPC.ready(userId, username, discriminator, avatar) function RPC.ready(userId, username, discriminator, avatar)
print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar)) print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar))
end end
function RPC.disconnected(errorCode, message) function RPC.disconnected(errorCode, message)
print(string.format("Discord: disconnected (%d: %s)", errorCode, message)) print(string.format("Discord: disconnected (%d: %s)", errorCode, message))
end end
function RPC.errored(errorCode, message) function RPC.errored(errorCode, message)
print(string.format("Discord: error (%d: %s)", errorCode, message)) print(string.format("Discord: error (%d: %s)", errorCode, message))
end end
function RPC.joinGame(joinSecret) function RPC.joinGame(joinSecret)
print(string.format("Discord: join (%s)", joinSecret)) print(string.format("Discord: join (%s)", joinSecret))
end end
function RPC.spectateGame(spectateSecret) function RPC.spectateGame(spectateSecret)
print(string.format("Discord: spectate (%s)", spectateSecret)) print(string.format("Discord: spectate (%s)", spectateSecret))
end end
function RPC.joinRequest(userId, username, discriminator, avatar) function RPC.joinRequest(userId, username, discriminator, avatar)
print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar)) print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar))
RPC.respond(userId, "yes") RPC.respond(userId, "yes")
end end
RPC.initialize(DiscordRPC.appId, true) RPC.initialize(DiscordRPC.appId, true)
local now = os.time(os.date("*t")) local now = os.time(os.date("*t"))
DiscordRPC.RPC = RPC DiscordRPC.RPC = RPC
print("DiscordRPC successfully loaded.") print("DiscordRPC successfully loaded.")
else else
print("DiscordRPC failed to load!") print("DiscordRPC failed to load!")
print(RPC) print(RPC)
end end
DiscordRPC.presence = { DiscordRPC.presence = {
startTimestamp = now, startTimestamp = now,
details = "Loading game...", details = "Loading game...",
state = "", state = "",
largeImageKey = "icon2", largeImageKey = "icon2",
largeImageText = "Arcade Stacker", largeImageText = "Arcade Stacker",
smallImageKey = "", smallImageKey = "",
smallImageText = "" smallImageText = ""
} }
function DiscordRPC:update(newstuff) function DiscordRPC:update(newstuff)
for k, v in pairs(newstuff) do self.presence[k] = v end for k, v in pairs(newstuff) do self.presence[k] = v end
if self.loaded then self.RPC.updatePresence(self.presence) end if self.loaded then self.RPC.updatePresence(self.presence) end
end end

150
main.lua
View File

@ -15,21 +15,42 @@ function love.load()
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
if not config.gamesettings then config.gamesettings = {} end if not config.gamesettings then config.gamesettings = {} end
for _, option in ipairs(GameConfigScene.options) do for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1 config.gamesettings[option[1]] = 1
end end
end end
if not config.input then if not config.input then
config.input = {}
scene = InputConfigScene() scene = InputConfigScene()
else else
if config.current_mode then current_mode = config.current_mode end if config.current_mode then current_mode = config.current_mode end
if config.current_ruleset then current_ruleset = config.current_ruleset end if config.current_ruleset then current_ruleset = config.current_ruleset end
scene = TitleScene() scene = TitleScene()
end end
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
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
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
end
end
--sort mode/rule lists
local function padnum(d) return ("%03d%s"):format(#d, d) end
table.sort(game_modes, function(a,b)
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
table.sort(rulesets, function(a,b)
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
end end
local TARGET_FPS = 60 local TARGET_FPS = 60
@ -88,13 +109,124 @@ function love.draw()
love.graphics.pop() love.graphics.pop()
end end
function love.keypressed(key, scancode, isrepeat) function love.keypressed(key, scancode)
-- global hotkeys -- global hotkeys
if scancode == "f4" then if scancode == "f4" then
config["fullscreen"] = not config["fullscreen"] config["fullscreen"] = not config["fullscreen"]
love.window.setFullscreen(config["fullscreen"]) love.window.setFullscreen(config["fullscreen"])
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
scene = InputConfigScene()
-- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return
-- escape is reserved for menu_back
elseif scancode == "escape" then
scene:onInputPress({input="menu_back", type="key", key=key, scancode=scancode})
-- pass any other key to the scene, with its configured mapping
else else
scene:onKeyPress({key=key, scancode=scancode, isRepeat=isrepeat}) local input_pressed = nil
if config.input and config.input.keys then
input_pressed = config.input.keys[scancode]
end
scene:onInputPress({input=input_pressed, type="key", key=key, scancode=scancode})
end
end
function love.keyreleased(key, scancode)
-- escape is reserved for menu_back
if scancode == "escape" then
scene:onInputRelease({input="menu_back", type="key", key=key, scancode=scancode})
-- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return
-- handle all other keys; tab is reserved, but the input config scene keeps it from getting configured as a game input, so pass tab to the scene here
else
local input_released = nil
if config.input and config.input.keys then
input_released = config.input.keys[scancode]
end
scene:onInputRelease({input=input_released, type="key", key=key, scancode=scancode})
end
end
function love.joystickpressed(joystick, button)
local input_pressed = nil
if
config.input and
config.input.joysticks and
config.input.joysticks[joystick:getName()] and
config.input.joysticks[joystick:getName()].buttons
then
input_pressed = config.input.joysticks[joystick:getName()].buttons[button]
end
scene:onInputPress({input=input_pressed, type="joybutton", name=joystick:getName(), button=button})
end
function love.joystickreleased(joystick, button)
local input_released = nil
if
config.input and
config.input.joysticks and
config.input.joysticks[joystick:getName()] and
config.input.joysticks[joystick:getName()].buttons
then
input_released = config.input.joysticks[joystick:getName()].buttons[button]
end
scene:onInputRelease({input=input_released, type="joybutton", name=joystick:getName(), button=button})
end
function love.joystickaxis(joystick, axis, value)
local input_pressed = nil
local positive_released = nil
local negative_released = nil
if
config.input and
config.input.joysticks and
config.input.joysticks[joystick:getName()] and
config.input.joysticks[joystick:getName()].axes and
config.input.joysticks[joystick:getName()].axes[axis]
then
if math.abs(value) >= 0.5 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"]
end
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
end
if math.abs(value) >= 0.5 then
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
else
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
scene:onInputRelease({input=negative_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
end
end
function love.joystickhat(joystick, hat, direction)
local input_pressed = nil
local has_hat = false
if
config.input and
config.input.joysticks and
config.input.joysticks[joystick:getName()] and
config.input.joysticks[joystick:getName()].hats and
config.input.joysticks[joystick:getName()].hats[hat]
then
if direction ~= "c" then
input_pressed = config.input.joysticks[joystick:getName()].hats[hat][direction]
end
has_hat = true
end
if input_pressed then
scene:onInputPress({input=input_pressed, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
elseif has_hat then
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
elseif direction ~= "c" then
scene:onInputPress({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
else
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
end end
end end

2
package.bat Normal file
View File

@ -0,0 +1,2 @@
tar -a -c -f cambridge.zip libs/binser.lua libs/classic.lua libs/discordRPC.lua load res scene tetris conf.lua main.lua scene.lua funcs.lua
rename cambridge.zip cambridge.love

26
release.bat Normal file
View File

@ -0,0 +1,26 @@
call package.bat
mkdir dist
mkdir dist\windows
mkdir dist\windows\libs
mkdir dist\win32
mkdir dist\win32\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 libs\discord-rpc.dll dist\windows\libs
copy libs\discord-rpc.dll dist\win32\libs
copy SOURCES.md dist\windows
copy LICENSE.md dist\windows
copy SOURCES.md dist\win32
copy LICENSE.md dist\win32
cd dist\windows
tar -a -c -f ..\cambridge-windows.zip cambridge.exe *.dll libs *.md
cd ..\..
cd dist\win32
tar -a -c -f ..\cambridge-win32.zip cambridge.exe *.dll libs *.md
cd ..\..

BIN
res/img/bonew.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
res/img/s8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

@ -5,7 +5,8 @@ Scene = Object:extend()
function Scene:new() end function Scene:new() end
function Scene:update() end function Scene:update() end
function Scene:render() end function Scene:render() end
function Scene:onKeyPress() end function Scene:onInputPress() end
function Scene:onInputRelease() end
ExitScene = require "scene.exit" ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"

View File

@ -17,7 +17,7 @@ function ConfigScene:changeOption(rel)
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1 self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
end end
function ConfigScene:onKeyPress(e) function ConfigScene:onInputPress(e)
end end
return ConfigScene return ConfigScene

View File

@ -7,7 +7,7 @@ function ExitScene:new()
end end
function ExitScene:update() function ExitScene:update()
love.event.quit() love.event.quit()
end end
function ExitScene:render() function ExitScene:render()
@ -16,7 +16,7 @@ end
function ExitScene:changeOption(rel) function ExitScene:changeOption(rel)
end end
function ExitScene:onKeyPress(e) function ExitScene:onInputPress(e)
end end
return ExitScene return ExitScene

View File

@ -1,30 +1,40 @@
local GameScene = Scene:extend() local GameScene = Scene:extend()
GameScene.title = "Game"
require 'load.save' require 'load.save'
function GameScene:new(game_mode, ruleset) function GameScene:new(game_mode, ruleset)
self.retry_mode = game_mode
self.retry_ruleset = ruleset
self.game = game_mode() self.game = game_mode()
self.ruleset = ruleset() self.ruleset = ruleset()
self.game:initialize(self.ruleset) self.game:initialize(self.ruleset)
self.inputs = {
left=false,
right=false,
up=false,
down=false,
rotate_left=false,
rotate_left2=false,
rotate_right=false,
rotate_right2=false,
rotate_180=false,
hold=false,
}
DiscordRPC:update({ DiscordRPC:update({
details = self.game.rpc_details, details = self.game.rpc_details,
state = self.game.name, state = self.game.name,
}) })
end end
function GameScene:update() function GameScene:update()
if love.window.hasFocus() then if love.window.hasFocus() then
self.game:update({ local inputs = {}
left = love.keyboard.isScancodeDown(config.input.left), for input, value in pairs(self.inputs) do
right = love.keyboard.isScancodeDown(config.input.right), inputs[input] = value
up = love.keyboard.isScancodeDown(config.input.up), end
down = love.keyboard.isScancodeDown(config.input.down), self.game:update(inputs, self.ruleset)
rotate_left = love.keyboard.isScancodeDown(config.input.rotate_left),
rotate_left2 = love.keyboard.isScancodeDown(config.input.rotate_left2),
rotate_right = love.keyboard.isScancodeDown(config.input.rotate_right),
rotate_right2 = love.keyboard.isScancodeDown(config.input.rotate_right2),
rotate_180 = love.keyboard.isScancodeDown(config.input.rotate_180),
hold = love.keyboard.isScancodeDown(config.input.hold),
}, self.ruleset)
end end
self.game.grid:update() self.game.grid:update()
@ -60,23 +70,24 @@ function GameScene:render()
end end
function GameScene:onKeyPress(e) function GameScene:onInputPress(e)
if (self.game.completed) and if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
(e.scancode == "return" or e.scancode == "escape") and e.isRepeat == false then
highscore_entry = self.game:getHighscoreData() highscore_entry = self.game:getHighscoreData()
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
submitHighscore(highscore_hash, highscore_entry) submitHighscore(highscore_hash, highscore_entry)
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) or ModeSelectScene()
elseif e.input == "retry" then
scene = GameScene(self.retry_mode, self.retry_ruleset)
elseif e.input == "menu_back" then
scene = ModeSelectScene() scene = ModeSelectScene()
elseif (e.scancode == config.input.retry) then elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
-- fuck this, this is hacky but the way this codebase is setup prevents anything else self.inputs[e.input] = true
-- it seems like all the values that get touched in the child gamemode class end
-- stop being linked to the values of the GameMode superclass because of how `mt.__index` works end
-- not even sure this is the actual problem, but I don't want to have to rebuild everything about
-- the core organisation of everything. this hacky way will have to do until someone figures out something. function GameScene:onInputRelease(e)
love.keypressed("escape", "escape", false) if e.input and string.sub(e.input, 1, 5) ~= "menu_" then
love.keypressed("return", "return", false) self.inputs[e.input] = false
elseif e.scancode == "escape" then
scene = ModeSelectScene()
end end
end end

View File

@ -5,10 +5,10 @@ ConfigScene.title = "Game Settings"
require 'load.save' require 'load.save'
ConfigScene.options = { ConfigScene.options = {
-- this serves as reference to what the options' values mean i guess? -- this serves as reference to what the options' values mean i guess?
{"manlock", "Manual locking", {"Per ruleset","Per gamemode","Harddrop", "Softdrop"}}, {"manlock", "Manual locking",{"Per ruleset","Per gamemode","Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", {"Per ruleset", "Arika", "TTC"}}, {"piece_colour", "Piece Colours", {"Per ruleset","Arika" ,"TTC"}},
{"world_reverse", "World Reverse", {"No", "Yes"}}, {"world_reverse","A Button Rotation", {"Left" ,"Auto" ,"Right"}},
} }
local optioncount = #ConfigScene.options local optioncount = #ConfigScene.options
@ -18,9 +18,9 @@ function ConfigScene:new()
self.highlight = 1 self.highlight = 1
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = "Changing game settings", state = "Changing game settings",
}) })
end end
function ConfigScene:update() function ConfigScene:update()
@ -35,44 +35,44 @@ function ConfigScene:render()
) )
love.graphics.setFont(font_3x5_4) love.graphics.setFont(font_3x5_4)
love.graphics.print("GAME SETTINGS", 80, 40) love.graphics.print("GAME SETTINGS", 80, 40)
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22) love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22)
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for i, option in ipairs(ConfigScene.options) do for i, option in ipairs(ConfigScene.options) do
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left") love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left")
for j, setting in ipairs(option[3]) do for j, setting in ipairs(option[3]) do
love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5) love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5)
love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center") love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center")
end end
end end
end end
function ConfigScene:onKeyPress(e) function ConfigScene:onInputPress(e)
if e.scancode == "return" and e.isRepeat == false then if e.input == "menu_decide" or e.scancode == "return" then
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = TitleScene() scene = TitleScene()
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then elseif e.input == "up" or e.scancode == "up" then
playSE("cursor") playSE("cursor")
self.highlight = Mod1(self.highlight-1, optioncount) self.highlight = Mod1(self.highlight-1, optioncount)
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then elseif e.input == "down" or e.scancode == "down" then
playSE("cursor") playSE("cursor")
self.highlight = Mod1(self.highlight+1, optioncount) self.highlight = Mod1(self.highlight+1, optioncount)
elseif (e.scancode == config.input["left"] or e.scancode == "left") and e.isRepeat == false then elseif e.input == "left" or e.scancode == "left" then
playSE("cursor_lr") playSE("cursor_lr")
local option = ConfigScene.options[self.highlight] local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3]) config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3])
elseif (e.scancode == config.input["right"] or e.scancode == "right") and e.isRepeat == false then elseif e.input == "right" or e.scancode == "right" then
playSE("cursor_lr") playSE("cursor_lr")
local option = ConfigScene.options[self.highlight] local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3]) config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3])
elseif e.scancode == "escape" then elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave() loadSave()
scene = TitleScene() scene = TitleScene()
end end
end end

View File

@ -5,28 +5,38 @@ ConfigScene.title = "Input Config"
require 'load.save' require 'load.save'
local configurable_inputs = { local configurable_inputs = {
"menu_decide",
"menu_back",
"left", "left",
"right", "right",
"up", "up",
"down", "down",
"rotate_left", "rotate_left",
"rotate_left2", "rotate_left2",
"rotate_right", "rotate_right",
"rotate_right2", "rotate_right2",
"rotate_180", "rotate_180",
"hold", "hold",
"retry", "retry",
} }
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function ConfigScene:new() function ConfigScene:new()
-- load current config
self.config = config.input
self.input_state = 1 self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = "Changing input config", state = "Changing input config",
}) })
end end
function ConfigScene:update() function ConfigScene:update()
@ -41,39 +51,106 @@ function ConfigScene:render()
) )
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for i, input in pairs(configurable_inputs) do for i, input in ipairs(configurable_inputs) do
love.graphics.printf(input, 40, 50 + i * 20, 200, "left") love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if config.input[input] then if self.set_inputs[input] then
love.graphics.printf( love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left")
love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")",
240, 50 + i * 20, 200, "left"
)
end end
end end
if self.input_state > table.getn(configurable_inputs) then if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete to retry") love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else else
love.graphics.print("press key for " .. configurable_inputs[self.input_state]) love.graphics.print("press key or joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip" .. (config.input and ", escape to cancel" or ""), 0, 0)
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
end end
end end
function ConfigScene:onKeyPress(e) local function addJoystick(input, name)
if self.input_state > table.getn(configurable_inputs) then if not input.joysticks then
if e.scancode == "return" then input.joysticks = {}
-- save, then load next scene end
saveConfig() if not input.joysticks[name] then
input.joysticks[name] = {}
end
end
function ConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" and config.input then
-- cancel only if there was an input config already
scene = TitleScene() scene = TitleScene()
elseif e.scancode == "delete" or e.scancode == "backspace" then elseif self.input_state > table.getn(configurable_inputs) then
self.input_state = 1 if e.scancode == "return" then
-- save new input, then load next scene
config.input = self.new_input
saveConfig()
scene = TitleScene()
elseif e.scancode == "delete" or e.scancode == "backspace" then
-- retry
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
end
elseif e.scancode == "tab" then
self.set_inputs[configurable_inputs[self.input_state]] = "skipped"
self.input_state = self.input_state + 1
elseif e.scancode ~= "escape" then
-- all other keys can be configured
if not self.new_input.keys then
self.new_input.keys = {}
end
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
self.new_input.keys[e.scancode] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
elseif string.sub(e.type, 1, 3) == "joy" then
if self.input_state <= table.getn(configurable_inputs) then
if e.type == "joybutton" then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].buttons then
self.new_input.joysticks[e.name].buttons = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jbtn " ..
e.button ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].buttons[e.button] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
elseif e.type == "joyaxis" then
if math.abs(e.value) >= 0.5 then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].axes then
self.new_input.joysticks[e.name].axes = {}
end
if not self.new_input.joysticks[e.name].axes[e.axis] then
self.new_input.joysticks[e.name].axes[e.axis] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jaxis " ..
(e.value >= 0.5 and "+" or "-") .. e.axis ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].axes[e.axis][e.value >= 0.5 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
elseif e.type == "joyhat" then
if e.direction ~= "c" then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].hats then
self.new_input.joysticks[e.name].hats = {}
end
if not self.new_input.joysticks[e.name].hats[e.hat] then
self.new_input.joysticks[e.name].hats[e.hat] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jhat " ..
e.hat .. " " .. e.direction ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end end
else
if e.scancode == "escape" then
loadSave()
scene = TitleScene()
else
config.input[configurable_inputs[self.input_state]] = e.scancode
self.input_state = self.input_state + 1
end
end end
end end

View File

@ -5,44 +5,6 @@ ModeSelectScene.title = "Game Start"
current_mode = 1 current_mode = 1
current_ruleset = 1 current_ruleset = 1
game_modes = {
require 'tetris.modes.marathon_2020',
require 'tetris.modes.survival_2020',
require 'tetris.modes.ck',
--require 'tetris.modes.strategy',
--require 'tetris.modes.interval_training',
--require 'tetris.modes.pacer_test',
require 'tetris.modes.demon_mode',
require 'tetris.modes.phantom_mania',
require 'tetris.modes.phantom_mania2',
require 'tetris.modes.phantom_mania_n',
require 'tetris.modes.race_40',
require 'tetris.modes.marathon_a1',
require 'tetris.modes.marathon_a2',
require 'tetris.modes.marathon_a3',
require 'tetris.modes.marathon_ax4',
require 'tetris.modes.marathon_c89',
require 'tetris.modes.survival_a1',
require 'tetris.modes.survival_a2',
require 'tetris.modes.survival_a3',
require 'tetris.modes.big_a2',
require 'tetris.modes.konoha',
}
rulesets = {
require 'tetris.rulesets.cambridge',
require 'tetris.rulesets.arika',
require 'tetris.rulesets.arika_ti',
require 'tetris.rulesets.ti_srs',
require 'tetris.rulesets.arika_ace',
require 'tetris.rulesets.arika_ace2',
require 'tetris.rulesets.arika_srs',
require 'tetris.rulesets.standard_exp',
--require 'tetris.rulesets.bonkers',
--require 'tetris.rulesets.shirase',
--require 'tetris.rulesets.super302',
}
function ModeSelectScene:new() function ModeSelectScene:new()
self.menu_state = { self.menu_state = {
mode = current_mode, mode = current_mode,
@ -50,9 +12,9 @@ function ModeSelectScene:new()
select = "mode", select = "mode",
} }
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = "Choosing a mode", state = "Choosing a mode",
}) })
end end
function ModeSelectScene:update() function ModeSelectScene:update()
@ -70,14 +32,14 @@ function ModeSelectScene:render()
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
end end
love.graphics.rectangle("fill", 20, 78 + 20 * self.menu_state.mode, 240, 22) love.graphics.rectangle("fill", 20, 258, 240, 22)
if self.menu_state.select == "mode" then if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.25) love.graphics.setColor(1, 1, 1, 0.25)
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
end end
love.graphics.rectangle("fill", 340, 78 + 20 * self.menu_state.ruleset, 200, 22) love.graphics.rectangle("fill", 340, 258, 200, 22)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@ -85,15 +47,19 @@ function ModeSelectScene:render()
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do for idx, mode in pairs(game_modes) do
love.graphics.printf(mode.name, 40, 80 + 20 * idx, 200, "left") if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
end
end end
for idx, ruleset in pairs(rulesets) do for idx, ruleset in pairs(rulesets) do
love.graphics.printf(ruleset.name, 360, 80 + 20 * idx, 160, "left") if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
end
end end
end end
function ModeSelectScene:onKeyPress(e) function ModeSelectScene:onInputPress(e)
if e.scancode == "return" and e.isRepeat == false then if e.input == "menu_decide" or e.scancode == "return" then
current_mode = self.menu_state.mode current_mode = self.menu_state.mode
current_ruleset = self.menu_state.ruleset current_ruleset = self.menu_state.ruleset
config.current_mode = current_mode config.current_mode = current_mode
@ -101,18 +67,17 @@ function ModeSelectScene:onKeyPress(e)
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset]) scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset])
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1) self:changeOption(-1)
playSE("cursor") playSE("cursor")
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1) self:changeOption(1)
playSE("cursor") playSE("cursor")
elseif (e.scancode == config.input["left"] or e.scancode == "left") or elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then
(e.scancode == config.input["right"] or e.scancode == "right") then
self:switchSelect() self:switchSelect()
playSE("cursor_lr") playSE("cursor_lr")
elseif e.scancode == "escape" then elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene() scene = TitleScene()
end end
end end

View File

@ -4,30 +4,30 @@ local main_menu_screens = {
ModeSelectScene, ModeSelectScene,
InputConfigScene, InputConfigScene,
GameConfigScene, GameConfigScene,
ExitScene, ExitScene,
} }
local mainmenuidle = { local mainmenuidle = {
"Idle", "Idle",
"On title screen", "On title screen",
"On main menu screen", "On main menu screen",
"Twiddling their thumbs", "Twiddling their thumbs",
"Admiring the main menu's BG", "Admiring the main menu's BG",
"Waiting for spring to come", "Waiting for spring to come",
"Actually not playing", "Actually not playing",
"Contemplating collecting stars", "Contemplating collecting stars",
"Preparing to put the block!!", "Preparing to put the block!!",
"Having a nap", "Having a nap",
"In menus", "In menus",
"Bottom text", "Bottom text",
} }
function TitleScene:new() function TitleScene:new()
self.main_menu_state = 1 self.main_menu_state = 1
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = mainmenuidle[math.random(#mainmenuidle)], state = mainmenuidle[math.random(#mainmenuidle)],
}) })
end end
function TitleScene:update() function TitleScene:update()
@ -57,18 +57,18 @@ function TitleScene:changeOption(rel)
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1 self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
end end
function TitleScene:onKeyPress(e) function TitleScene:onInputPress(e)
if e.scancode == "return" and e.isRepeat == false then if e.input == "menu_decide" or e.scancode == "return" then
playSE("main_decide") playSE("main_decide")
scene = main_menu_screens[self.main_menu_state]() scene = main_menu_screens[self.main_menu_state]()
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1) self:changeOption(-1)
playSE("cursor") playSE("cursor")
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1) self:changeOption(1)
playSE("cursor") playSE("cursor")
elseif e.scancode == "escape" and e.isRepeat == false then elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
love.event.quit() love.event.quit()
end end
end end

View File

@ -4,6 +4,7 @@ local Grid = Object:extend()
local empty = { skin = "", colour = "" } local empty = { skin = "", colour = "" }
local oob = { skin = "", colour = "" } local oob = { skin = "", colour = "" }
local block = { skin = "2tie", colour = "A" }
function Grid:new() function Grid:new()
self.grid = {} self.grid = {}
@ -28,10 +29,10 @@ function Grid:clear()
end end
function Grid:getCell(x, y) function Grid:getCell(x, y)
if x < 1 or x > 10 or y > 24 then return oob if x < 1 or x > 10 or y > 24 then return oob
elseif y < 1 then return empty elseif y < 1 then return empty
else return self.grid[y][x] else return self.grid[y][x]
end end
end end
function Grid:isOccupied(x, y) function Grid:isOccupied(x, y)
@ -66,11 +67,11 @@ function Grid:canPlaceBigPiece(piece)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
local x = piece.position.x + offset.x local x = piece.position.x + offset.x
local y = piece.position.y + offset.y local y = piece.position.y + offset.y
if ( if (
self:isOccupied(x * 2 + 0, y * 2 + 0) self:isOccupied(x * 2 + 0, y * 2 + 0)
or self:isOccupied(x * 2 + 1, y * 2 + 0) or self:isOccupied(x * 2 + 1, y * 2 + 0)
or self:isOccupied(x * 2 + 0, y * 2 + 1) or self:isOccupied(x * 2 + 0, y * 2 + 1)
or self:isOccupied(x * 2 + 1, y * 2 + 1) or self:isOccupied(x * 2 + 1, y * 2 + 1)
) then ) then
return false return false
end end
@ -141,14 +142,35 @@ function Grid:copyBottomRow()
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty} self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for col = 1, 10 do for col = 1, 10 do
self.grid[24][col] = (self.grid[23][col] == empty) and empty or { self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
skin = self.grid[23][col].skin,
colour = "X"
}
end end
return true return true
end end
function Grid:garbageRise(row_vals)
for row = 1, 23 do
self.grid[row] = self.grid[row+1]
self.grid_age[row] = self.grid_age[row+1]
end
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for col = 1, 10 do
self.grid[24][col] = (row_vals[col] == "e") and empty or block
end
end
function Grid:applyFourWide()
for row = 1, 24 do
local x = self.grid[row]
x[1] = x[1]~=block and block or x[1]
x[2] = x[2]~=block and block or x[2]
x[3] = x[3]~=block and block or x[3]
x[8] = x[8]~=block and block or x[8]
x[9] = x[9]~=block and block or x[9]
x[10] = x[10]~=block and block or x[10]
end
end
function Grid:applyPiece(piece) function Grid:applyPiece(piece)
if piece.big then if piece.big then
self:applyBigPiece(piece) self:applyBigPiece(piece)
@ -174,12 +196,12 @@ function Grid:applyBigPiece(piece)
y = piece.position.y + offset.y y = piece.position.y + offset.y
for a = 1, 2 do for a = 1, 2 do
for b = 1, 2 do for b = 1, 2 do
if y*2+a > 0 then if y*2+a > 0 then
self.grid[y*2+a][x*2+b] = { self.grid[y*2+a][x*2+b] = {
skin = piece.skin, skin = piece.skin,
colour = piece.colour colour = piece.colour
} }
end end
end end
end end
end end
@ -187,45 +209,45 @@ end
function Grid:checkForBravo(cleared_row_count) function Grid:checkForBravo(cleared_row_count)
for i = 0, 23 - cleared_row_count do for i = 0, 23 - cleared_row_count do
for j = 0, 9 do for j = 0, 9 do
if self:isOccupied(j, i) then return false end if self:isOccupied(j, i) then return false end
end end
end end
return true return true
end end
function Grid:checkSecretGrade() function Grid:checkSecretGrade()
local sgrade = 0 local sgrade = 0
for i=23,5,-1 do for i=23,5,-1 do
local validLine = true local validLine = true
local emptyCell = 0 local emptyCell = 0
if i > 13 then if i > 13 then
emptyCell = 23-i emptyCell = 23-i
end end
if i <= 13 then if i <= 13 then
emptyCell = i-5 emptyCell = i-5
end end
for j=0,9 do for j=0,9 do
if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then
validLine = false validLine = false
end end
end end
if not self:isOccupied(emptyCell,i-1) then if not self:isOccupied(emptyCell,i-1) then
validLine = false validLine = false
end end
if(validLine) then if(validLine) then
sgrade = sgrade + 1 sgrade = sgrade + 1
else else
-- return sgrade return sgrade
end end
end end
--[[ --[[
if(sgrade == 0) then return "" if(sgrade == 0) then return ""
elseif(sgrade < 10) then return 10-sgrade elseif(sgrade < 10) then return 10-sgrade
elseif(sgrade < 19) then return "S"..(sgrade-9) end elseif(sgrade < 19) then return "S"..(sgrade-9) end
return "GM" return "GM"
--]] --]]
return sgrade return sgrade
end end
function Grid:update() function Grid:update()
@ -239,14 +261,18 @@ function Grid:update()
end end
function Grid:draw() function Grid:draw()
for y = 1, 24 do for y = 5, 24 do
for x = 1, 10 do for x = 1, 10 do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
if self.grid_age[y][x] < 1 then if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
else else
love.graphics.setColor(0.5, 0.5, 0.5, 1) if self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1)
else
love.graphics.setColor(0.5, 0.5, 0.5, 1)
end
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
end end
if self.grid[y][x].skin ~= "bone" then if self.grid[y][x].skin ~= "bone" then
@ -271,12 +297,12 @@ function Grid:draw()
end end
function Grid:drawInvisible(opacity_function, garbage_opacity_function) function Grid:drawInvisible(opacity_function, garbage_opacity_function)
for y = 1, 24 do for y = 5, 24 do
for x = 1, 10 do for x = 1, 10 do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
opacity = 1 opacity = 1
elseif garbage_opacity_function and self.grid[y][x].colour == "G" then elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
opacity = garbage_opacity_function(self.grid_age[y][x]) opacity = garbage_opacity_function(self.grid_age[y][x])
else else
opacity = opacity_function(self.grid_age[y][x]) opacity = opacity_function(self.grid_age[y][x])

View File

@ -10,7 +10,7 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay
self.gravity = gravity self.gravity = gravity
self.lock_delay = lock_delay self.lock_delay = lock_delay
self.skin = skin self.skin = skin
self.colour = colour self.colour = colour
self.ghost = false self.ghost = false
self.locked = false self.locked = false
self.big = big self.big = big
@ -98,7 +98,7 @@ end
function Piece:dropToBottom(grid) function Piece:dropToBottom(grid)
local piece_y = self.position.y local piece_y = self.position.y
self:dropSquares(24, grid) self:dropSquares(math.huge, grid)
self.gravity = 0 self.gravity = 0
if self.position.y > piece_y then if self.position.y > piece_y then
-- if it got dropped any, also reset lock delay -- if it got dropped any, also reset lock delay

View File

@ -15,99 +15,95 @@ MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible r
function MarathonA2Game:new() function MarathonA2Game:new()
self.super:new() self.super:new()
self.big_mode = true self.big_mode = true
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.randomizer = History6RollsRandomizer()
self.grade = 0 self.grade = 0
self.grade_points = 0 self.grade_points = 0
self.grade_point_decay_counter = 0 self.grade_point_decay_counter = 0
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = false self.lock_drop = false
self.lock_hard_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
function MarathonA2Game:getARE() function MarathonA2Game:getARE()
if self.level < 700 then return 27 if self.level < 700 then return 27
elseif self.level < 800 then return 18 elseif self.level < 800 then return 18
else return 14 end else return 14 end
end end
function MarathonA2Game:getLineARE() function MarathonA2Game:getLineARE()
if self.level < 600 then return 27 if self.level < 600 then return 27
elseif self.level < 700 then return 18 elseif self.level < 700 then return 18
elseif self.level < 800 then return 14 elseif self.level < 800 then return 14
else return 8 end else return 8 end
end end
function MarathonA2Game:getDasLimit() function MarathonA2Game:getDasLimit()
if self.level < 500 then return 15 if self.level < 500 then return 15
elseif self.level < 900 then return 9 elseif self.level < 900 then return 9
else return 7 end else return 7 end
end end
function MarathonA2Game:getLineClearDelay() function MarathonA2Game:getLineClearDelay()
if self.level < 500 then return 40 if self.level < 500 then return 40
elseif self.level < 600 then return 25 elseif self.level < 600 then return 25
elseif self.level < 700 then return 16 elseif self.level < 700 then return 16
elseif self.level < 800 then return 12 elseif self.level < 800 then return 12
else return 6 end else return 6 end
end end
function MarathonA2Game:getLockDelay() function MarathonA2Game:getLockDelay()
if self.level < 900 then return 30 if self.level < 900 then return 30
else return 17 end else return 17 end
end end
function MarathonA2Game:getGravity() function MarathonA2Game:getGravity()
if (self.level < 30) then return 4/256 if (self.level < 30) then return 4/256
elseif (self.level < 35) then return 6/256 elseif (self.level < 35) then return 6/256
elseif (self.level < 40) then return 8/256 elseif (self.level < 40) then return 8/256
elseif (self.level < 50) then return 10/256 elseif (self.level < 50) then return 10/256
elseif (self.level < 60) then return 12/256 elseif (self.level < 60) then return 12/256
elseif (self.level < 70) then return 16/256 elseif (self.level < 70) then return 16/256
elseif (self.level < 80) then return 32/256 elseif (self.level < 80) then return 32/256
elseif (self.level < 90) then return 48/256 elseif (self.level < 90) then return 48/256
elseif (self.level < 100) then return 64/256 elseif (self.level < 100) then return 64/256
elseif (self.level < 120) then return 80/256 elseif (self.level < 120) then return 80/256
elseif (self.level < 140) then return 96/256 elseif (self.level < 140) then return 96/256
elseif (self.level < 160) then return 112/256 elseif (self.level < 160) then return 112/256
elseif (self.level < 170) then return 128/256 elseif (self.level < 170) then return 128/256
elseif (self.level < 200) then return 144/256 elseif (self.level < 200) then return 144/256
elseif (self.level < 220) then return 4/256 elseif (self.level < 220) then return 4/256
elseif (self.level < 230) then return 32/256 elseif (self.level < 230) then return 32/256
elseif (self.level < 233) then return 64/256 elseif (self.level < 233) then return 64/256
elseif (self.level < 236) then return 96/256 elseif (self.level < 236) then return 96/256
elseif (self.level < 239) then return 128/256 elseif (self.level < 239) then return 128/256
elseif (self.level < 243) then return 160/256 elseif (self.level < 243) then return 160/256
elseif (self.level < 247) then return 192/256 elseif (self.level < 247) then return 192/256
elseif (self.level < 251) then return 224/256 elseif (self.level < 251) then return 224/256
elseif (self.level < 300) then return 1 elseif (self.level < 300) then return 1
elseif (self.level < 330) then return 2 elseif (self.level < 330) then return 2
elseif (self.level < 360) then return 3 elseif (self.level < 360) then return 3
elseif (self.level < 400) then return 4 elseif (self.level < 400) then return 4
elseif (self.level < 420) then return 5 elseif (self.level < 420) then return 5
elseif (self.level < 450) then return 4 elseif (self.level < 450) then return 4
elseif (self.level < 500) then return 3 elseif (self.level < 500) then return 3
else return 20 else return 20
end end
end end
function MarathonA2Game:advanceOneFrame() function MarathonA2Game:advanceOneFrame()
if self.clear then if self.clear then
self.roll_frames = self.roll_frames + 1 self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then return false end
if self.roll_frames > 3694 then if self.roll_frames > 3694 then
self.completed = true self.completed = true
if self.grade == 32 then
self.grade = 33
end
end end
elseif self.ready_frames == 0 then elseif self.ready_frames == 0 then
self.frames = self.frames + 1 self.frames = self.frames + 1
@ -123,43 +119,31 @@ end
function MarathonA2Game:onLineClear(cleared_row_count) function MarathonA2Game:onLineClear(cleared_row_count)
cleared_row_count = cleared_row_count / 2 cleared_row_count = cleared_row_count / 2
self:updateSectionTimes(self.level, self.level + cleared_row_count) self.level = math.min(self.level + cleared_row_count, 999)
self.level = math.min(self.level + cleared_row_count, 999) if self.level == 999 and not self.clear then
if self.level == 999 and not self.clear then self.clear = true
self.clear = true self.grid:clear()
if self:qualifiesForMRoll() then self.roll_frames = -150
self.grade = 32 end
end self.lock_drop = self.level >= 900
self.grid:clear() self.lock_hard_drop = self.level >= 900
self.roll_frames = -150
end
end end
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end if not self.clear then
cleared_lines = cleared_lines / 2 cleared_lines = cleared_lines / 2
self:updateGrade(cleared_lines) self:updateGrade(cleared_lines)
if cleared_lines > 0 then if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + 2 * drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * self.combo * self.bravo self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo * self.bravo
self.combo = self.combo + 2 * (cleared_lines - 1) )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end
end
function MarathonA2Game:updateSectionTimes(old_level, new_level)
if self.clear then return end
if math.floor(old_level / 100) < math.floor(new_level / 100) or
new_level >= 999 then
-- record new section
section_time = self.frames - self.section_start_time
self.section_times[math.floor(old_level / 100)] = section_time
self.section_start_time = self.frames
end end
end end
@ -224,7 +208,7 @@ local grade_conversion = {
1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
7, 8, 8, 8, 9, 9, 9, 10, 11, 12, 7, 8, 8, 8, 9, 9, 9, 10, 11, 12,
12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17, 18, 19 17
} }
function MarathonA2Game:updateGrade(cleared_lines) function MarathonA2Game:updateGrade(cleared_lines)
@ -249,49 +233,12 @@ function MarathonA2Game:updateGrade(cleared_lines)
end end
end end
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
function MarathonA2Game:qualifiesForMRoll()
if not self.clear then return false end
-- tetris requirements
for section = 0, 9 do
if self.section_tetrises[section] < tetris_requirements[section] then
return false
end
end
-- section time requirements
local section_average = 0
for section = 0, 4 do
section_average = section_average + self.section_times[section]
if self.section_times[section] > frameTime(1,05) then
return false
end
end
-- section time average requirements
if self.section_times[5] > section_average / 5 then
return false
end
for section = 6, 9 do
if self.section_times[section] > self.section_times[section - 1] + 120 then
return false
end
end
if self.grade < 17 or self.frames > frameTime(8,45) then
return false
end
return true
end
function MarathonA2Game:getLetterGrade() function MarathonA2Game:getLetterGrade()
local grade = grade_conversion[self.grade] local grade = grade_conversion[self.grade]
if grade < 9 then if grade < 9 then
return tostring(9 - grade) return tostring(9 - grade)
elseif grade < 18 then elseif grade < 18 then
return "S" .. tostring(grade - 8) return "S" .. tostring(grade - 8)
elseif grade == 18 then
return "M"
else
return "GM"
end end
end end
@ -301,18 +248,9 @@ MarathonA2Game.rollOpacityFunction = function(age)
else return 1 - (age - 240) / 60 end else return 1 - (age - 240) / 60 end
end end
MarathonA2Game.mRollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function MarathonA2Game:drawGrid(ruleset) function MarathonA2Game:drawGrid(ruleset)
if self.clear and not (self.completed or self.game_over) then if self.clear and not (self.completed or self.game_over) then
if self:qualifiesForMRoll() then self.grid:drawInvisible(self.rollOpacityFunction)
self.grid:drawInvisible(self.mRollOpacityFunction)
else
self.grid:drawInvisible(self.rollOpacityFunction)
end
else else
self.grid:draw() self.grid:draw()
if self.piece ~= nil and self.level < 100 then if self.piece ~= nil and self.level < 100 then
@ -336,7 +274,10 @@ function MarathonA2Game:drawScoringInfo()
love.graphics.printf("LEVEL", 240, 320, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left") love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")

View File

@ -1,325 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SurvivalCKGame = GameMode:extend()
SurvivalCKGame.name = "Survival CK"
SurvivalCKGame.hash = "SurvivalCK"
SurvivalCKGame.tagline = "An endurance mode created by CylinderKnot! Watch out for the fading pieces..."
function SurvivalCKGame:new()
SurvivalCKGame.super:new()
self.garbage = 0
self.roll_frames = 0
self.combo = 1
self.grade = 0
self.level = 0
self.randomizer = History6RollsRandomizer()
self.enable_hold = true
self.next_queue_length = 3
self.coolregret_timer = 0
end
function SurvivalCKGame:getARE()
if self.level < 100 then return 15
elseif self.level < 200 then return 14
elseif self.level < 300 then return 13
elseif self.level < 400 then return 12
elseif self.level < 500 then return 11
elseif self.level < 600 then return 10
elseif self.level < 700 then return 9
elseif self.level < 800 then return 8
elseif self.level < 900 then return 7
elseif self.level < 1000 then return 6
elseif self.level < 2500 then return 5
else return 7 end
end
function SurvivalCKGame:getLineARE()
return SurvivalCKGame:getARE()
end
function SurvivalCKGame:getDasLimit()
if self.level < 700 then return 10
elseif self.level < 900 then return 9
elseif self.level < 1100 then return 8
elseif self.level < 1300 then return 7
elseif self.level < 1600 then return 6
else return 5 end
end
function SurvivalCKGame:getLineClearDelay()
if self.level < 100 then return 10
elseif self.level < 200 then return 8
elseif self.level < 300 then return 7
elseif self.level < 400 then return 6
else return 5 end
end
function SurvivalCKGame:getLockDelay()
if self.level < 600 then return 20
elseif self.level < 700 then return 19
elseif self.level < 800 then return 18
elseif self.level < 900 then return 17
elseif self.level < 1000 then return 16
elseif self.level < 1200 then return 15
elseif self.level < 1400 then return 14
elseif self.level < 1700 then return 13
elseif self.level < 2100 then return 12
elseif self.level < 2200 then return 11
elseif self.level < 2300 then return 10
elseif self.level < 2400 then return 9
elseif self.level < 2500 then return 8
else return 15 end
end
function SurvivalCKGame:getGravity()
return 20
end
function SurvivalCKGame:getGarbageLimit()
if self.level < 1000 then return 20
elseif self.level < 1100 then return 17
elseif self.level < 1200 then return 14
elseif self.level < 1300 then return 11
else return 8 end
end
function SurvivalCKGame:getRegretTime()
if self.level < 500 then return frameTime(0,55)
elseif self.level < 1000 then return frameTime(0,50)
elseif self.level < 1500 then return frameTime(0,40)
elseif self.level < 2000 then return frameTime(0,35)
else return frameTime(0,30) end
end
function SurvivalCKGame:getNextPiece(ruleset)
return {
skin = self.level >= 2000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end
local torikan_times = {300, 330, 360, 390, 420, 450, 478, 504, 528, 550, 570}
function SurvivalCKGame:hitTorikan(old_level, new_level)
for i = 1, 11 do
if old_level < (900 + i * 100) and new_level >= (900 + i * 100) and self.frames > torikan_times[i] * 60 then
self.level = 900 + i * 100
return true
end
end
return false
end
function SurvivalCKGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
if self.roll_frames + 1 == 0 then
switchBGM("credit_roll", "gm3")
return true
end
return false
elseif self.roll_frames > 3238 then
switchBGM(nil)
if self.grade ~= 20 then self.grade = self.grade + 1 end
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function SurvivalCKGame:onPieceEnter()
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function SurvivalCKGame:onLineClear(cleared_row_count)
if not self.clear then
local new_level = self.level + cleared_row_count * 2
self:updateSectionTimes(self.level, new_level)
if new_level >= 2500 or self:hitTorikan(self.level, new_level) then
self.clear = true
if new_level >= 2500 then
self.level = 2500
self.grid:clear()
self.big_mode = true
self.roll_frames = -150
end
else
self.level = math.min(new_level, 2500)
end
self:advanceBottomRow(-cleared_row_count)
end
end
function SurvivalCKGame:onPieceLock(piece, cleared_row_count)
if cleared_row_count == 0 then self:advanceBottomRow(1) end
end
function SurvivalCKGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
function SurvivalCKGame:updateSectionTimes(old_level, new_level)
if math.floor(old_level / 100) < math.floor(new_level / 100) then
local section = math.floor(old_level / 100) + 1
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
if section_time <= self:getRegretTime(self.level) then
self.grade = self.grade + 1
else
self.coolregret_message = "REGRET!!"
self.coolregret_timer = 300
end
end
end
function SurvivalCKGame:advanceBottomRow(dx)
if self.level >= 1000 and self.level < 1500 then
self.garbage = math.max(self.garbage + dx, 0)
if self.garbage >= self:getGarbageLimit() then
self.grid:copyBottomRow()
self.garbage = 0
end
end
end
function SurvivalCKGame:drawGrid()
if self.level >= 1500 and self.level < 1600 then
self.grid:drawInvisible(self.rollOpacityFunction1)
elseif self.level >= 1600 and self.level < 1700 then
self.grid:drawInvisible(self.rollOpacityFunction2)
elseif self.level >= 1700 and self.level < 1800 then
self.grid:drawInvisible(self.rollOpacityFunction3)
elseif self.level >= 1800 and self.level < 1900 then
self.grid:drawInvisible(self.rollOpacityFunction4)
elseif self.level >= 1900 and self.level < 2000 then
self.grid:drawInvisible(self.rollOpacityFunction5)
else
self.grid:draw()
end
end
-- screw trying to make this work efficiently
-- lua function variables are so garbage
SurvivalCKGame.rollOpacityFunction1 = function(age)
if age < 420 then return 1
elseif age > 480 then return 0
else return 1 - (age - 420) / 60 end
end
SurvivalCKGame.rollOpacityFunction2 = function(age)
if age < 360 then return 1
elseif age > 420 then return 0
else return 1 - (age - 360) / 60 end
end
SurvivalCKGame.rollOpacityFunction3 = function(age)
if age < 300 then return 1
elseif age > 360 then return 0
else return 1 - (age - 300) / 60 end
end
SurvivalCKGame.rollOpacityFunction4 = function(age)
if age < 240 then return 1
elseif age > 300 then return 0
else return 1 - (age - 240) / 60 end
end
SurvivalCKGame.rollOpacityFunction5 = function(age)
if age < 180 then return 1
elseif age > 240 then return 0
else return 1 - (age - 180) / 60 end
end
local master_grades = { "M", "MK", "MV", "MO", "MM" }
function SurvivalCKGame:getLetterGrade()
if self.grade == 0 then
return "1"
elseif self.grade < 10 then
return "S" .. tostring(self.grade)
elseif self.grade < 21 then
return "m" .. tostring(self.grade - 9)
elseif self.grade < 26 then
return master_grades[self.grade - 20]
else
return "GM"
end
end
function SurvivalCKGame:drawScoringInfo()
SurvivalCKGame.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("GRADE", text_x, 120, 40, "left")
love.graphics.printf("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
if (self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
self.coolregret_timer = self.coolregret_timer - 1
end
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self:getLetterGrade(self.grade), text_x, 140, 90, "left")
love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 50, "right")
if self.clear then
love.graphics.printf(self.level, text_x, 370, 50, "right")
else
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
end
end
function SurvivalCKGame:getHighscoreData()
return {
grade = self.grade,
level = self.level,
frames = self.frames,
}
end
function SurvivalCKGame:getSectionEndLevel()
return math.floor(self.level / 100 + 1) * 100
end
function SurvivalCKGame:getBackground()
return math.min(math.floor(self.level / 100), 19)
end
return SurvivalCKGame

View File

@ -1,265 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local DemonModeGame = GameMode:extend()
DemonModeGame.name = "Demon Mode"
DemonModeGame.hash = "DemonMode"
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
function DemonModeGame:new()
DemonModeGame.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.grade = 0
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.section_tetris_count = 0
self.section_tries = 0
self.enable_hold = true
self.lock_drop = true
self.next_queue_length = 3
if math.random() < 1/6.66 then
self.rpc_details = "Suffering"
end
end
function DemonModeGame:getARE()
if self.level < 500 then return 30
elseif self.level < 600 then return 25
elseif self.level < 700 then return 15
elseif self.level < 800 then return 14
elseif self.level < 900 then return 12
elseif self.level < 1000 then return 11
elseif self.level < 1100 then return 10
elseif self.level < 1300 then return 8
elseif self.level < 1400 then return 6
elseif self.level < 1700 then return 4
elseif self.level < 1800 then return 3
elseif self.level < 1900 then return 2
elseif self.level < 2000 then return 1
else return 0 end
end
function DemonModeGame:getLineARE()
return self:getARE()
end
function DemonModeGame:getDasLimit()
if self.level < 500 then return 15
elseif self.level < 1000 then return 10
elseif self.level < 1500 then return 5
elseif self.level < 1700 then return 4
elseif self.level < 1900 then return 3
elseif self.level < 2000 then return 2
else return 1 end
end
function DemonModeGame:getLineClearDelay()
if self.level < 600 then return 15
elseif self.level < 800 then return 10
elseif self.level < 1000 then return 8
elseif self.level < 1500 then return 5
elseif self.level < 1700 then return 3
elseif self.level < 1900 then return 2
elseif self.level < 2000 then return 1
else return 0 end
end
function DemonModeGame:getLockDelay()
if self.level < 100 then return 30
elseif self.level < 200 then return 25
elseif self.level < 300 then return 22
elseif self.level < 400 then return 20
elseif self.level < 1000 then return 15
elseif self.level < 1200 then return 10
elseif self.level < 1400 then return 9
elseif self.level < 1500 then return 8
elseif self.level < 1600 then return 7
elseif self.level < 1700 then return 6
elseif self.level < 1800 then return 5
elseif self.level < 1900 then return 4
elseif self.level < 2000 then return 3
else return 2 end
end
function DemonModeGame:getGravity()
return 20
end
local function getSectionForLevel(level)
return math.floor(level / 100) + 1
end
local cleared_row_levels = {1, 3, 6, 10}
function DemonModeGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then
return false
elseif self.roll_frames >= 1337 then
self.completed = true
end
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
end
function DemonModeGame:onPieceEnter()
if (self.level % 100 ~= 99) and self.frames ~= 0 then
self.level = self.level + 1
end
end
function DemonModeGame:onLineClear(cleared_row_count)
if cleared_row_count == 4 then
self.section_tetris_count = self.section_tetris_count + 1
end
local advanced_levels = cleared_row_levels[cleared_row_count]
if not self.clear then
self:updateSectionTimes(self.level, self.level + advanced_levels)
end
end
function DemonModeGame:updateSectionTimes(old_level, new_level)
local section = math.floor(old_level / 100) + 1
if math.floor(old_level / 100) < math.floor(new_level / 100) then
-- If at least one Tetris in this section hasn't been made,
-- deny section passage.
if old_level > 500 then
if self.section_tetris_count == 0 then
self.level = 100 * math.floor(old_level / 100)
self.section_tries = self.section_tries + 1
else
self.level = math.min(new_level, 2500)
-- if this is first try (no denials, add a grade)
if self.section_tries == 0 then
self.grade = self.grade + 1
end
self.section_tries = 0
self.section_tetris_count = 0
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
-- maybe clear
if self.level == 2500 and not self.clear then
self.clear = true
self.grid:clear()
self.roll_frames = -150
end
end
elseif old_level < 100 then
-- If section time is under cutoff, skip to level 500.
if self.frames < frameTime(1,00) then
self.level = 500
self.grade = 5
self.section_tries = 0
self.section_tetris_count = 0
else
self.level = math.min(new_level, 2500)
self.skip_failed = true
end
-- record new section
section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time)
self.section_start_time = self.frames
else
self.level = math.min(new_level, 2500)
if self.skip_failed and new_level >= 500 then
self.level = 500
self.game_over = true
end
end
else
self.level = math.min(new_level, 2500)
end
end
function DemonModeGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + (
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + cleared_lines - 1
else
self.drop_bonus = 0
self.combo = 1
end
end
local letter_grades = {
[0] = "", "D", "C", "B", "A",
"S", "S-A", "S-B", "S-C", "S-D",
"X", "X-A", "X-B", "X-C", "X-D",
"W", "W-A", "W-B", "W-C", "W-D",
"Master", "MasterS", "MasterX", "MasterW", "Grand Master",
"Demon Master"
}
function DemonModeGame:getLetterGrade()
return letter_grades[self.grade]
end
function DemonModeGame:drawGrid()
if self.clear and not (self.completed or self.game_over) then
self.grid:drawInvisible(self.rollOpacityFunction)
else
self.grid:draw()
end
end
DemonModeGame.rollOpacityFunction = function(age)
if age > 4 then return 0
else return 1 - age / 4 end
end
function DemonModeGame:drawScoringInfo()
DemonModeGame.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")
if self.grade ~= 0 then love.graphics.printf("GRADE", 240, 120, 40, "left") end
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
-- draw section time data
local current_section = getSectionForLevel(self.level)
self:drawSectionTimesWithSecondary(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
love.graphics.printf(string.format("%.2f", self.level / 100), 240, 340, 70, "right")
end
function DemonModeGame:getHighscoreData()
return {
grade = self.grade,
level = self.level,
frames = self.frames,
}
end
function DemonModeGame:getBackground()
return math.min(math.floor(self.level / 100), 19)
end
return DemonModeGame

View File

@ -43,12 +43,12 @@ function GameMode:new()
self.draw_section_times = false self.draw_section_times = false
self.draw_secondary_section_times = false self.draw_secondary_section_times = false
self.big_mode = false self.big_mode = false
self.rpc_details = "In game" self.rpc_details = "In game"
-- variables related to configurable parameters -- variables related to configurable parameters
self.drop_locked = false self.drop_locked = false
self.hard_drop_locked = false self.hard_drop_locked = false
self.lock_on_soft_drop = false self.lock_on_soft_drop = false
self.lock_on_hard_drop = false self.lock_on_hard_drop = false
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
self.section_start_time = 0 self.section_start_time = 0
@ -75,12 +75,12 @@ end
function GameMode:initialize(ruleset) function GameMode:initialize(ruleset)
-- generate next queue -- generate next queue
self:new() self:new()
for i = 1, self.next_queue_length do for i = 1, self.next_queue_length do
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock] self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock] self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock]
end end
function GameMode:update(inputs, ruleset) function GameMode:update(inputs, ruleset)
@ -369,7 +369,7 @@ function GameMode:drawGhostPiece(ruleset)
end end
function GameMode:drawNextQueue(ruleset) function GameMode:drawNextQueue(ruleset)
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour] local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
function drawPiece(piece, skin, offsets, pos_x, pos_y) function drawPiece(piece, skin, offsets, pos_x, pos_y)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
local x = offset.x + ruleset.spawn_positions[piece].x local x = offset.x + ruleset.spawn_positions[piece].x
@ -389,7 +389,8 @@ function GameMode:drawNextQueue(ruleset)
end end
end end
if self.hold_queue ~= nil then if self.hold_queue ~= nil then
self:setHoldOpacity() local hold_color = self.held and 0.6 or 1
self:setHoldOpacity(1, hold_color)
drawPiece( drawPiece(
self.hold_queue.shape, self.hold_queue.shape,
self.hold_queue.skin, self.hold_queue.skin,
@ -400,8 +401,16 @@ function GameMode:drawNextQueue(ruleset)
return false return false
end end
function GameMode:setNextOpacity(i) love.graphics.setColor(1, 1, 1, 1) end function GameMode:setNextOpacity(i, j)
function GameMode:setHoldOpacity() love.graphics.setColor(1, 1, 1, 1) end i = i ~= nil and i or 1
j = j ~= nil and j or 1
love.graphics.setColor(j, j, j, i)
end
function GameMode:setHoldOpacity(i, j)
i = i ~= nil and i or 1
j = j ~= nil and j or 1
love.graphics.setColor(j, j, j, i)
end
function GameMode:drawScoringInfo() function GameMode:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)

View File

@ -1,155 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local IntervalTrainingGame = GameMode:extend()
IntervalTrainingGame.name = "Interval Training"
IntervalTrainingGame.hash = "IntervalTraining"
IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
function IntervalTrainingGame:new()
IntervalTrainingGame.super:new()
self.roll_frames = 0
self.combo = 1
self.randomizer = History6RollsRandomizer()
self.section_time_limit = 1800
self.section_start_time = 0
self.section_times = { [0] = 0 }
self.lock_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function IntervalTrainingGame:getARE()
return 4
end
function IntervalTrainingGame:getLineARE()
return 4
end
function IntervalTrainingGame:getDasLimit()
return 6
end
function IntervalTrainingGame:getLineClearDelay()
return 6
end
function IntervalTrainingGame:getLockDelay()
return 15
end
function IntervalTrainingGame:getGravity()
return 20
end
function IntervalTrainingGame:getSection()
return math.floor(level / 100) + 1
end
function IntervalTrainingGame:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 2968 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
if self:getSectionTime() >= self.section_time_limit then
self.game_over = true
end
end
return true
end
function IntervalTrainingGame:onPieceEnter()
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1
end
end
function IntervalTrainingGame:onLineClear(cleared_row_count)
if not self.clear then
local new_level = self.level + cleared_row_count
self:updateSectionTimes(self.level, new_level)
self.level = math.min(new_level, 999)
if self.level == 999 then
self.clear = true
end
end
end
function IntervalTrainingGame:getSectionTime()
return self.frames - self.section_start_time
end
function IntervalTrainingGame:updateSectionTimes(old_level, new_level)
if math.floor(old_level / 100) < math.floor(new_level / 100) then
-- record new section
table.insert(self.section_times, self:getSectionTime())
self.section_start_time = self.frames
else
self.level = math.min(new_level, 999)
end
end
function IntervalTrainingGame:drawGrid(ruleset)
self.grid:draw()
end
function IntervalTrainingGame:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function IntervalTrainingGame:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
strTrueValues(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
local current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.level, 240, 340, 40, "right")
-- draw time left, flash red if necessary
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
end
function IntervalTrainingGame:getSectionEndLevel()
if self.level >= 900 then return 999
else return math.floor(self.level / 100 + 1) * 100 end
end
function IntervalTrainingGame:getBackground()
return math.floor(self.level / 100)
end
return IntervalTrainingGame

View File

@ -1,190 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local KonohaRandomizer = require 'tetris.randomizers.bag_konoha'
local KonohaGame = GameMode:extend()
KonohaGame.name = "All Clear A4"
KonohaGame.hash = "AllClearA4"
KonohaGame.tagline = "Get as many bravos as you can under the time limit!"
function KonohaGame:new()
KonohaGame.super:new()
self.randomizer = KonohaRandomizer()
self.bravos = 0
self.last_bonus_amount = 0
self.last_bonus_display_time = 0
self.time_limit = 10800
self.big_mode = true
self.enable_hold = true
self.next_queue_length = 3
end
function KonohaGame:getARE()
if self.level < 300 then return 30
elseif self.level < 400 then return 25
elseif self.level < 500 then return 20
elseif self.level < 600 then return 17
elseif self.level < 800 then return 15
elseif self.level < 900 then return 13
elseif self.level < 1000 then return 10
elseif self.level < 1300 then return 8
else return 6 end
end
function KonohaGame:getLineARE()
return self:getARE()
end
function KonohaGame:getDasLimit()
if self.level < 500 then return 10
elseif self.level < 800 then return 9
elseif self.level < 1000 then return 8
else return 7 end
end
function KonohaGame:getLineClearDelay()
if self.level < 200 then return 14
elseif self.level < 500 then return 9
elseif self.level < 800 then return 8
elseif self.level < 1000 then return 7
else return 6 end
end
function KonohaGame:getLockDelay()
if self.level < 500 then return 30
elseif self.level < 600 then return 25
elseif self.level < 700 then return 23
elseif self.level < 800 then return 20
elseif self.level < 900 then return 17
elseif self.level < 1000 then return 15
elseif self.level < 1200 then return 13
elseif self.level < 1300 then return 10
else return 8 end
end
function KonohaGame:getGravity()
if (self.level < 30) then return 4/256
elseif (self.level < 35) then return 8/256
elseif (self.level < 40) then return 12/256
elseif (self.level < 50) then return 16/256
elseif (self.level < 60) then return 32/256
elseif (self.level < 70) then return 48/256
elseif (self.level < 80) then return 64/256
elseif (self.level < 90) then return 128/256
elseif (self.level < 100) then return 192/256
elseif (self.level < 120) then return 1
elseif (self.level < 140) then return 2
elseif (self.level < 160) then return 3
elseif (self.level < 170) then return 4
elseif (self.level < 200) then return 5
else return 20 end
end
function KonohaGame:getSection()
return math.floor(level / 100) + 1
end
function KonohaGame:getSectionEndLevel()
return math.floor(self.level / 100 + 1) * 100
end
function KonohaGame:advanceOneFrame()
if self.ready_frames == 0 then
self.time_limit = self.time_limit - 1
self.frames = self.frames + 1
end
if self.time_limit <= 0 then
self.game_over = true
end
self.last_bonus_display_time = self.last_bonus_display_time - 1
end
function KonohaGame:onPieceEnter()
if (self.level % 100 ~= 99) and self.frames ~= 0 then
self.level = self.level + 1
end
end
function KonohaGame:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
local cleared_row_levels = {2, 4, 6, 12}
local bravo_bonus = {300, 480, 660, 900}
local non_bravo_bonus = {0, 0, 20, 40}
local bravo_ot_bonus = {0, 60, 120, 180}
function KonohaGame:onLineClear(cleared_row_count)
local oldtime = self.time_limit
self.level = self.level + cleared_row_levels[cleared_row_count / 2]
if self.grid:checkForBravo(cleared_row_count) then
self.bravos = self.bravos + 1
if self.level < 1000 then self.time_limit = self.time_limit + bravo_bonus[cleared_row_count / 2]
else self.time_limit = self.time_limit + bravo_ot_bonus[cleared_row_count / 2]
end
if self.bravos == 11 then self.randomizer.allowrepeat = true end
elseif self.level < 1000 then
self.time_limit = self.time_limit + non_bravo_bonus[cleared_row_count / 2]
end
local bonus = self.time_limit - oldtime
if bonus > 0 then
self.last_bonus_amount = bonus
self.last_bonus_display_time = 120
end
end
function KonohaGame:getBackground()
return math.floor(self.level / 100)
end
function KonohaGame:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
strTrueValues(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("TIME LIMIT", 240, 120, 120, "left")
love.graphics.printf("BRAVOS", 240, 200, 50, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
love.graphics.setFont(font_3x5_3)
if not self.game_over and self.time_limit < frameTime(0,10) and self.time_limit % 4 < 2 then
love.graphics.setColor(1, 0.3, 0.3, 1)
end
love.graphics.printf(formatTime(self.time_limit), 240, 140, 120, "right")
love.graphics.setColor(1, 1, 1, 1)
if self.last_bonus_display_time > 0 then
love.graphics.printf("+"..formatTime(self.last_bonus_amount), 240, 160, 120, "right")
end
love.graphics.printf(self.bravos, 240, 220, 90, "left")
love.graphics.printf(self.level, 240, 340, 50, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 50, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function KonohaGame:getHighscoreData()
return {
bravos = self.bravos,
level = self.level,
frames = self.frames,
}
end
return KonohaGame

View File

@ -36,10 +36,12 @@ function Marathon2020Game:new()
self.grade_points = 0 self.grade_points = 0
self.grade_point_decay_counter = 0 self.grade_point_decay_counter = 0
self.max_grade_points = 0 self.max_grade_points = 0
self.cool_timer = 0
end end
function Marathon2020Game:getARE() function Marathon2020Game:getARE()
if self.delay_level < 1 then return 27 if self.delay_level < 1 then return 27
elseif self.delay_level < 2 then return 24 elseif self.delay_level < 2 then return 24
elseif self.delay_level < 3 then return 21 elseif self.delay_level < 3 then return 21
elseif self.delay_level < 4 then return 18 elseif self.delay_level < 4 then return 18
@ -58,7 +60,7 @@ function Marathon2020Game:getLineARE()
end end
function Marathon2020Game:getDasLimit() function Marathon2020Game:getDasLimit()
if self.delay_level < 1 then return 15 if self.delay_level < 1 then return 15
elseif self.delay_level < 3 then return 12 elseif self.delay_level < 3 then return 12
elseif self.delay_level < 5 then return 9 elseif self.delay_level < 5 then return 9
elseif self.delay_level < 8 then return 8 elseif self.delay_level < 8 then return 8
@ -70,7 +72,7 @@ function Marathon2020Game:getDasLimit()
end end
function Marathon2020Game:getLineClearDelay() function Marathon2020Game:getLineClearDelay()
if self.delay_level < 1 then return 40 if self.delay_level < 1 then return 40
elseif self.delay_level < 3 then return 25 elseif self.delay_level < 3 then return 25
elseif self.delay_level < 4 then return 20 elseif self.delay_level < 4 then return 20
elseif self.delay_level < 5 then return 15 elseif self.delay_level < 5 then return 15
@ -82,7 +84,7 @@ function Marathon2020Game:getLineClearDelay()
end end
function Marathon2020Game:getLockDelay() function Marathon2020Game:getLockDelay()
if self.delay_level < 6 then return 30 if self.delay_level < 6 then return 30
elseif self.delay_level < 7 then return 26 elseif self.delay_level < 7 then return 26
elseif self.delay_level < 8 then return 22 elseif self.delay_level < 8 then return 22
elseif self.delay_level < 9 then return 19 elseif self.delay_level < 9 then return 19
@ -96,35 +98,35 @@ function Marathon2020Game:getLockDelay()
end end
function Marathon2020Game:getGravity() function Marathon2020Game:getGravity()
if self.level < 30 then return 4/256 if self.level < 30 then return 4/256
elseif self.level < 35 then return 6/256 elseif self.level < 35 then return 6/256
elseif self.level < 40 then return 8/256 elseif self.level < 40 then return 8/256
elseif self.level < 50 then return 10/256 elseif self.level < 50 then return 10/256
elseif self.level < 60 then return 12/256 elseif self.level < 60 then return 12/256
elseif self.level < 70 then return 16/256 elseif self.level < 70 then return 16/256
elseif self.level < 80 then return 32/256 elseif self.level < 80 then return 32/256
elseif self.level < 90 then return 48/256 elseif self.level < 90 then return 48/256
elseif self.level < 100 then return 64/256 elseif self.level < 100 then return 64/256
elseif self.level < 120 then return 80/256 elseif self.level < 120 then return 80/256
elseif self.level < 140 then return 96/256 elseif self.level < 140 then return 96/256
elseif self.level < 160 then return 112/256 elseif self.level < 160 then return 112/256
elseif self.level < 170 then return 128/256 elseif self.level < 170 then return 128/256
elseif self.level < 200 then return 144/256 elseif self.level < 200 then return 144/256
elseif self.level < 220 then return 4/256 elseif self.level < 220 then return 4/256
elseif self.level < 230 then return 32/256 elseif self.level < 230 then return 32/256
elseif self.level < 233 then return 64/256 elseif self.level < 233 then return 64/256
elseif self.level < 236 then return 96/256 elseif self.level < 236 then return 96/256
elseif self.level < 239 then return 128/256 elseif self.level < 239 then return 128/256
elseif self.level < 243 then return 160/256 elseif self.level < 243 then return 160/256
elseif self.level < 247 then return 192/256 elseif self.level < 247 then return 192/256
elseif self.level < 251 then return 224/256 elseif self.level < 251 then return 224/256
elseif self.level < 300 then return 1 elseif self.level < 300 then return 1
elseif self.level < 330 then return 2 elseif self.level < 330 then return 2
elseif self.level < 360 then return 3 elseif self.level < 360 then return 3
elseif self.level < 400 then return 4 elseif self.level < 400 then return 4
elseif self.level < 420 then return 5 elseif self.level < 420 then return 5
elseif self.level < 450 then return 4 elseif self.level < 450 then return 4
elseif self.level < 500 then return 3 elseif self.level < 500 then return 3
else return 20 end else return 20 end
end end
@ -258,7 +260,7 @@ local function getSectionForLevel(level)
end end
function Marathon2020Game:getEndOfSectionForSection(section) function Marathon2020Game:getEndOfSectionForSection(section)
if self.torikan_passed[900] == false and section == 10 then return 999 if self.torikan_passed[900] == false and section == 10 then return 999
elseif self.torikan_passed[1900] == false and section == 20 then return 2000 elseif self.torikan_passed[1900] == false and section == 20 then return 2000
elseif section == 20 then return 2020 elseif section == 20 then return 2020
else return section * 100 end else return section * 100 end
@ -301,7 +303,7 @@ function Marathon2020Game:checkClear(level)
level >= 2020 level >= 2020
) then ) then
if self.torikan_passed[500] == false then self.level = 500 if self.torikan_passed[500] == false then self.level = 500
elseif self.torikan_passed[900] == false then self.level = 999 elseif self.torikan_passed[900] == false then self.level = 999
elseif self.torikan_passed[1000] == false then self.level = 1000 elseif self.torikan_passed[1000] == false then self.level = 1000
elseif self.torikan_passed[1500] == false then self.level = 1500 elseif self.torikan_passed[1500] == false then self.level = 1500
@ -327,6 +329,7 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
self.section_cool_count = self.section_cool_count + 1 self.section_cool_count = self.section_cool_count + 1
self.delay_level = math.min(20, self.delay_level + 1) self.delay_level = math.min(20, self.delay_level + 1)
table.insert(self.section_status, "cool") table.insert(self.section_status, "cool")
self.cool_timer = 300
end end
local section = getSectionForLevel(old_level) local section = getSectionForLevel(old_level)
@ -430,6 +433,11 @@ function Marathon2020Game:drawScoringInfo()
self:drawSectionTimesWithSecondary(current_section) self:drawSectionTimesWithSecondary(current_section)
if (self.cool_timer > 0) then
love.graphics.printf("COOL!!", 64, 400, 160, "center")
self.cool_timer = self.cool_timer - 1
end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left") love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
love.graphics.printf(self.grade_points, text_x, 220, 90, "left") love.graphics.printf(self.grade_points, text_x, 220, 90, "left")

View File

@ -14,26 +14,26 @@ MarathonA1Game.tagline = "Can you score enough points to reach the title of Gran
function MarathonA1Game:new() function MarathonA1Game:new()
MarathonA1Game.super:new() MarathonA1Game.super:new()
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.bravos = 0 self.bravos = 0
self.gm_conditions = { self.gm_conditions = {
level300 = false, level300 = false,
level500 = false, level500 = false,
level999 = false level999 = false
} }
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.randomizer = History4RollsRandomizer() self.randomizer = History4RollsRandomizer()
self.lock_drop = false self.lock_drop = false
self.enable_hard_drop = false self.enable_hard_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
@ -59,60 +59,60 @@ function MarathonA1Game:getLockDelay()
end end
local function getRankForScore(score) local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400} if score < 400 then return {rank = "9", next = 400}
elseif score < 800 then return {rank = "8", next = 800} elseif score < 800 then return {rank = "8", next = 800}
elseif score < 1400 then return {rank = "7", next = 1400} elseif score < 1400 then return {rank = "7", next = 1400}
elseif score < 2000 then return {rank = "6", next = 2000} elseif score < 2000 then return {rank = "6", next = 2000}
elseif score < 3500 then return {rank = "5", next = 3500} elseif score < 3500 then return {rank = "5", next = 3500}
elseif score < 5500 then return {rank = "4", next = 5500} elseif score < 5500 then return {rank = "4", next = 5500}
elseif score < 8000 then return {rank = "3", next = 8000} elseif score < 8000 then return {rank = "3", next = 8000}
elseif score < 12000 then return {rank = "2", next = 12000} elseif score < 12000 then return {rank = "2", next = 12000}
elseif score < 16000 then return {rank = "1", next = 16000} elseif score < 16000 then return {rank = "1", next = 16000}
elseif score < 22000 then return {rank = "S1", next = 22000} elseif score < 22000 then return {rank = "S1", next = 22000}
elseif score < 30000 then return {rank = "S2", next = 30000} elseif score < 30000 then return {rank = "S2", next = 30000}
elseif score < 40000 then return {rank = "S3", next = 40000} elseif score < 40000 then return {rank = "S3", next = 40000}
elseif score < 52000 then return {rank = "S4", next = 52000} elseif score < 52000 then return {rank = "S4", next = 52000}
elseif score < 66000 then return {rank = "S5", next = 66000} elseif score < 66000 then return {rank = "S5", next = 66000}
elseif score < 82000 then return {rank = "S6", next = 82000} elseif score < 82000 then return {rank = "S6", next = 82000}
elseif score < 100000 then return {rank = "S7", next = 100000} elseif score < 100000 then return {rank = "S7", next = 100000}
elseif score < 120000 then return {rank = "S8", next = 120000} elseif score < 120000 then return {rank = "S8", next = 120000}
else return {rank = "S9", next = "???"} else return {rank = "S9", next = "???"}
end end
end end
function MarathonA1Game:getGravity() function MarathonA1Game:getGravity()
local level = self.level local level = self.level
if (level < 30) then return 4/256 if (level < 30) then return 4/256
elseif (level < 35) then return 6/256 elseif (level < 35) then return 6/256
elseif (level < 40) then return 8/256 elseif (level < 40) then return 8/256
elseif (level < 50) then return 10/256 elseif (level < 50) then return 10/256
elseif (level < 60) then return 12/256 elseif (level < 60) then return 12/256
elseif (level < 70) then return 16/256 elseif (level < 70) then return 16/256
elseif (level < 80) then return 32/256 elseif (level < 80) then return 32/256
elseif (level < 90) then return 48/256 elseif (level < 90) then return 48/256
elseif (level < 100) then return 64/256 elseif (level < 100) then return 64/256
elseif (level < 120) then return 80/256 elseif (level < 120) then return 80/256
elseif (level < 140) then return 96/256 elseif (level < 140) then return 96/256
elseif (level < 160) then return 112/256 elseif (level < 160) then return 112/256
elseif (level < 170) then return 128/256 elseif (level < 170) then return 128/256
elseif (level < 200) then return 144/256 elseif (level < 200) then return 144/256
elseif (level < 220) then return 4/256 elseif (level < 220) then return 4/256
elseif (level < 230) then return 32/256 elseif (level < 230) then return 32/256
elseif (level < 233) then return 64/256 elseif (level < 233) then return 64/256
elseif (level < 236) then return 96/256 elseif (level < 236) then return 96/256
elseif (level < 239) then return 128/256 elseif (level < 239) then return 128/256
elseif (level < 243) then return 160/256 elseif (level < 243) then return 160/256
elseif (level < 247) then return 192/256 elseif (level < 247) then return 192/256
elseif (level < 251) then return 224/256 elseif (level < 251) then return 224/256
elseif (level < 300) then return 1 elseif (level < 300) then return 1
elseif (level < 330) then return 2 elseif (level < 330) then return 2
elseif (level < 360) then return 3 elseif (level < 360) then return 3
elseif (level < 400) then return 4 elseif (level < 400) then return 4
elseif (level < 420) then return 5 elseif (level < 420) then return 5
elseif (level < 450) then return 4 elseif (level < 450) then return 4
elseif (level < 500) then return 3 elseif (level < 500) then return 3
else return 20 else return 20
end end
end end
function MarathonA1Game:advanceOneFrame() function MarathonA1Game:advanceOneFrame()
@ -134,47 +134,47 @@ function MarathonA1Game:onPieceEnter()
end end
function MarathonA1Game:onLineClear(cleared_row_count) function MarathonA1Game:onLineClear(cleared_row_count)
self:checkGMRequirements(self.level, self.level + cleared_row_count) self:checkGMRequirements(self.level, self.level + cleared_row_count)
if not self.clear then if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999) local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 then if new_level == 999 then
self.clear = true self.clear = true
else
self.level = new_level
end end
self.level = new_level
end end
end end
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
if self.grid:checkForBravo(cleared_lines) then if not self.clear then
self.bravo = 4 if self.grid:checkForBravo(cleared_lines) then
self.bravos = self.bravos + 1 self.bravo = 4
else self.bravo = 1 end self.bravos = self.bravos + 1
if cleared_lines > 0 then else self.bravo = 1 end
self.combo = self.combo + (cleared_lines - 1) * 2 if cleared_lines > 0 then
self.score = self.score + ( self.combo = self.combo + (cleared_lines - 1) * 2
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.score = self.score + (
cleared_lines * self.combo * self.bravo (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
) cleared_lines * self.combo * self.bravo
self.lines = self.lines + cleared_lines )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
function MarathonA1Game:checkGMRequirements(old_level, new_level) function MarathonA1Game:checkGMRequirements(old_level, new_level)
if old_level < 300 and new_level >= 300 then if old_level < 300 and new_level >= 300 then
if self.score > 12000 and self.frames <= frameTime(4,15) then if self.score >= 12000 and self.frames <= frameTime(4,15) then
self.gm_conditions["level300"] = true self.gm_conditions["level300"] = true
end end
elseif old_level < 500 and new_level >= 500 then elseif old_level < 500 and new_level >= 500 then
if self.score > 40000 and self.frames <= frameTime(7,30) then if self.score >= 40000 and self.frames <= frameTime(7,30) then
self.gm_conditions["level500"] = true self.gm_conditions["level500"] = true
end end
elseif old_level < 999 and new_level >= 999 then elseif old_level < 999 and new_level >= 999 then
if self.score > 126000 and self.frames <= frameTime(13,30) then if self.score >= 126000 and self.frames <= frameTime(13,30) then
self.gm_conditions["level900"] = true self.gm_conditions["level999"] = true
end end
end end
end end
@ -201,16 +201,16 @@ function MarathonA1Game:drawScoringInfo()
love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("NEXT RANK", 240, 260, 90, "left") love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.score, 240, 220, 90, "left")
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
love.graphics.printf("GM", 240, 140, 90, "left") love.graphics.printf("GM", 240, 140, 90, "left")
else else
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left") love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
@ -218,9 +218,9 @@ function MarathonA1Game:drawScoringInfo()
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left") love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end
love.graphics.setFont(font_8x11) love.graphics.setFont(font_8x11)

View File

@ -15,10 +15,10 @@ MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible r
function MarathonA2Game:new() function MarathonA2Game:new()
MarathonA2Game.super:new() MarathonA2Game.super:new()
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.grade = 0 self.grade = 0
self.grade_points = 0 self.grade_points = 0
@ -27,88 +27,88 @@ function MarathonA2Game:new()
self.section_times = { [0] = 0 } self.section_times = { [0] = 0 }
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.randomizer = History6RollsRandomizer() self.lock_drop = false
self.lock_hard_drop = false
self.lock_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
function MarathonA2Game:getARE() function MarathonA2Game:getARE()
if self.level < 700 then return 27 if self.level < 700 then return 27
elseif self.level < 800 then return 18 elseif self.level < 800 then return 18
else return 14 end else return 14 end
end end
function MarathonA2Game:getLineARE() function MarathonA2Game:getLineARE()
if self.level < 600 then return 27 if self.level < 600 then return 27
elseif self.level < 700 then return 18 elseif self.level < 700 then return 18
elseif self.level < 800 then return 14 elseif self.level < 800 then return 14
else return 8 end else return 8 end
end end
function MarathonA2Game:getDasLimit() function MarathonA2Game:getDasLimit()
if self.level < 500 then return 15 if self.level < 500 then return 15
elseif self.level < 900 then return 9 elseif self.level < 900 then return 9
else return 7 end else return 7 end
end end
function MarathonA2Game:getLineClearDelay() function MarathonA2Game:getLineClearDelay()
if self.level < 500 then return 40 if self.level < 500 then return 40
elseif self.level < 600 then return 25 elseif self.level < 600 then return 25
elseif self.level < 700 then return 16 elseif self.level < 700 then return 16
elseif self.level < 800 then return 12 elseif self.level < 800 then return 12
else return 6 end else return 6 end
end end
function MarathonA2Game:getLockDelay() function MarathonA2Game:getLockDelay()
if self.level < 900 then return 30 if self.level < 900 then return 30
else return 17 end else return 17 end
end end
function MarathonA2Game:getGravity() function MarathonA2Game:getGravity()
if (self.level < 30) then return 4/256 if (self.level < 30) then return 4/256
elseif (self.level < 35) then return 6/256 elseif (self.level < 35) then return 6/256
elseif (self.level < 40) then return 8/256 elseif (self.level < 40) then return 8/256
elseif (self.level < 50) then return 10/256 elseif (self.level < 50) then return 10/256
elseif (self.level < 60) then return 12/256 elseif (self.level < 60) then return 12/256
elseif (self.level < 70) then return 16/256 elseif (self.level < 70) then return 16/256
elseif (self.level < 80) then return 32/256 elseif (self.level < 80) then return 32/256
elseif (self.level < 90) then return 48/256 elseif (self.level < 90) then return 48/256
elseif (self.level < 100) then return 64/256 elseif (self.level < 100) then return 64/256
elseif (self.level < 120) then return 80/256 elseif (self.level < 120) then return 80/256
elseif (self.level < 140) then return 96/256 elseif (self.level < 140) then return 96/256
elseif (self.level < 160) then return 112/256 elseif (self.level < 160) then return 112/256
elseif (self.level < 170) then return 128/256 elseif (self.level < 170) then return 128/256
elseif (self.level < 200) then return 144/256 elseif (self.level < 200) then return 144/256
elseif (self.level < 220) then return 4/256 elseif (self.level < 220) then return 4/256
elseif (self.level < 230) then return 32/256 elseif (self.level < 230) then return 32/256
elseif (self.level < 233) then return 64/256 elseif (self.level < 233) then return 64/256
elseif (self.level < 236) then return 96/256 elseif (self.level < 236) then return 96/256
elseif (self.level < 239) then return 128/256 elseif (self.level < 239) then return 128/256
elseif (self.level < 243) then return 160/256 elseif (self.level < 243) then return 160/256
elseif (self.level < 247) then return 192/256 elseif (self.level < 247) then return 192/256
elseif (self.level < 251) then return 224/256 elseif (self.level < 251) then return 224/256
elseif (self.level < 300) then return 1 elseif (self.level < 300) then return 1
elseif (self.level < 330) then return 2 elseif (self.level < 330) then return 2
elseif (self.level < 360) then return 3 elseif (self.level < 360) then return 3
elseif (self.level < 400) then return 4 elseif (self.level < 400) then return 4
elseif (self.level < 420) then return 5 elseif (self.level < 420) then return 5
elseif (self.level < 450) then return 4 elseif (self.level < 450) then return 4
elseif (self.level < 500) then return 3 elseif (self.level < 500) then return 3
else return 20 else return 20
end end
end end
function MarathonA2Game:advanceOneFrame() function MarathonA2Game:advanceOneFrame()
if self.clear then if self.clear then
self.roll_frames = self.roll_frames + 1 self.roll_frames = self.roll_frames + 1
if self.roll_frames < 0 then return false end
if self.roll_frames > 3694 then if self.roll_frames > 3694 then
self.completed = true self.completed = true
if self.grade == 32 then if self.grade == 32 then
@ -127,33 +127,33 @@ function MarathonA2Game:onPieceEnter()
end end
end end
function MarathonA2Game:onLineClear(cleared_row_count) function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
self:updateSectionTimes(self.level, self.level + cleared_row_count) if not self.clear then
self.level = math.min(self.level + cleared_row_count, 999) self:updateGrade(cleared_lines)
if self.level == 999 and not self.clear then if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
self.clear = true if cleared_lines > 0 then
if self:qualifiesForMRoll() then self.combo = self.combo + (cleared_lines - 1) * 2
self.grade = 32 self.score = self.score + (
end (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.grid:clear() cleared_lines * self.combo * self.bravo
self.roll_frames = -150 )
end else
self.combo = 1
end
self.drop_bonus = 0
else self.lines = self.lines + cleared_lines end
end end
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA2Game:onLineClear(cleared_row_count)
self:updateGrade(cleared_lines) self.level = math.min(self.level + cleared_row_count, 999)
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end if self.level == 999 and not self.clear then
if cleared_lines > 0 then self.clear = true
self.score = self.score + ( self.grid:clear()
(math.ceil((level + cleared_lines) / 4) + 2 * drop_bonus) * if self:qualifiesForMRoll() then self.grade = 32 end
cleared_lines * self.combo * self.bravo self.roll_frames = -150
)
self.lines = self.lines + cleared_lines
self.combo = self.combo + (cleared_lines - 1) * 2
else
self.drop_bonus = 0
self.combo = 1
end end
self.lock_drop = self.level >= 900
self.lock_hard_drop = self.level >= 900
end end
function MarathonA2Game:updateSectionTimes(old_level, new_level) function MarathonA2Game:updateSectionTimes(old_level, new_level)
@ -253,7 +253,7 @@ function MarathonA2Game:updateGrade(cleared_lines)
end end
end end
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 } local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 0 }
function MarathonA2Game:qualifiesForMRoll() function MarathonA2Game:qualifiesForMRoll()
if not self.clear then return false end if not self.clear then return false end
@ -280,7 +280,7 @@ function MarathonA2Game:qualifiesForMRoll()
return false return false
end end
end end
if self.grade < 17 or self.frames > frameTime(9,30) then if self.grade < 31 or self.frames > frameTime(8,45) then
return false return false
end end
return true return true
@ -338,19 +338,29 @@ function MarathonA2Game:drawScoringInfo()
love.graphics.printf("GRADE", 240, 120, 40, "left") love.graphics.printf("GRADE", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
if self.clear then
if self:qualifiesForMRoll() then
if self.lines >= 32 and self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
else love.graphics.setColor(0, 1, 0, 1) end
else
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
else love.graphics.setColor(0, 1, 0, 1) end
end
end
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left") love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
love.graphics.setFont(font_8x11) love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")

View File

@ -15,11 +15,11 @@ MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all
function MarathonA3Game:new() function MarathonA3Game:new()
MarathonA3Game.super:new() MarathonA3Game.super:new()
self.speed_level = 0 self.speed_level = 0
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.grade = 0 self.grade = 0
self.grade_points = 0 self.grade_points = 0
self.roll_points = 0 self.roll_points = 0
@ -29,96 +29,99 @@ function MarathonA3Game:new()
self.section_start_time = 0 self.section_start_time = 0
self.section_70_times = { [0] = 0 } self.section_70_times = { [0] = 0 }
self.section_times = { [0] = 0 } self.section_times = { [0] = 0 }
self.section_cool = false
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
self.coolregret_message = "COOL!!" self.coolregret_message = "COOL!!"
self.coolregret_timer = 0 self.coolregret_timer = 0
self.torikan_passed = false
end end
function MarathonA3Game:getARE() function MarathonA3Game:getARE()
if self.speed_level < 700 then return 27 if self.speed_level < 700 then return 27
elseif self.speed_level < 800 then return 18 elseif self.speed_level < 800 then return 18
elseif self.speed_level < 1000 then return 14 elseif self.speed_level < 1000 then return 14
elseif self.speed_level < 1100 then return 8 elseif self.speed_level < 1100 then return 8
elseif self.speed_level < 1200 then return 7 elseif self.speed_level < 1200 then return 7
else return 6 end else return 6 end
end end
function MarathonA3Game:getLineARE() function MarathonA3Game:getLineARE()
if self.speed_level < 600 then return 27 if self.speed_level < 600 then return 27
elseif self.speed_level < 700 then return 18 elseif self.speed_level < 700 then return 18
elseif self.speed_level < 800 then return 14 elseif self.speed_level < 800 then return 14
elseif self.speed_level < 1100 then return 8 elseif self.speed_level < 1100 then return 8
elseif self.speed_level < 1200 then return 7 elseif self.speed_level < 1200 then return 7
else return 6 end else return 6 end
end end
function MarathonA3Game:getDasLimit() function MarathonA3Game:getDasLimit()
if self.speed_level < 500 then return 15 if self.speed_level < 500 then return 15
elseif self.speed_level < 900 then return 9 elseif self.speed_level < 900 then return 9
else return 7 end else return 7 end
end end
function MarathonA3Game:getLineClearDelay() function MarathonA3Game:getLineClearDelay()
if self.speed_level < 500 then return 40 if self.speed_level < 500 then return 40
elseif self.speed_level < 600 then return 25 elseif self.speed_level < 600 then return 25
elseif self.speed_level < 700 then return 16 elseif self.speed_level < 700 then return 16
elseif self.speed_level < 800 then return 12 elseif self.speed_level < 800 then return 12
elseif self.speed_level < 1100 then return 6 elseif self.speed_level < 1100 then return 6
elseif self.speed_level < 1200 then return 5 elseif self.speed_level < 1200 then return 5
else return 4 end else return 4 end
end end
function MarathonA3Game:getLockDelay() function MarathonA3Game:getLockDelay()
if self.speed_level < 900 then return 30 if self.speed_level < 900 then return 30
elseif self.speed_level < 1100 then return 17 elseif self.speed_level < 1100 then return 17
else return 15 end else return 15 end
end end
function MarathonA3Game:getGravity() function MarathonA3Game:getGravity()
if (self.speed_level < 30) then return 4/256 if (self.speed_level < 30) then return 4/256
elseif (self.speed_level < 35) then return 6/256 elseif (self.speed_level < 35) then return 6/256
elseif (self.speed_level < 40) then return 8/256 elseif (self.speed_level < 40) then return 8/256
elseif (self.speed_level < 50) then return 10/256 elseif (self.speed_level < 50) then return 10/256
elseif (self.speed_level < 60) then return 12/256 elseif (self.speed_level < 60) then return 12/256
elseif (self.speed_level < 70) then return 16/256 elseif (self.speed_level < 70) then return 16/256
elseif (self.speed_level < 80) then return 32/256 elseif (self.speed_level < 80) then return 32/256
elseif (self.speed_level < 90) then return 48/256 elseif (self.speed_level < 90) then return 48/256
elseif (self.speed_level < 100) then return 64/256 elseif (self.speed_level < 100) then return 64/256
elseif (self.speed_level < 120) then return 80/256 elseif (self.speed_level < 120) then return 80/256
elseif (self.speed_level < 140) then return 96/256 elseif (self.speed_level < 140) then return 96/256
elseif (self.speed_level < 160) then return 112/256 elseif (self.speed_level < 160) then return 112/256
elseif (self.speed_level < 170) then return 128/256 elseif (self.speed_level < 170) then return 128/256
elseif (self.speed_level < 200) then return 144/256 elseif (self.speed_level < 200) then return 144/256
elseif (self.speed_level < 220) then return 4/256 elseif (self.speed_level < 220) then return 4/256
elseif (self.speed_level < 230) then return 32/256 elseif (self.speed_level < 230) then return 32/256
elseif (self.speed_level < 233) then return 64/256 elseif (self.speed_level < 233) then return 64/256
elseif (self.speed_level < 236) then return 96/256 elseif (self.speed_level < 236) then return 96/256
elseif (self.speed_level < 239) then return 128/256 elseif (self.speed_level < 239) then return 128/256
elseif (self.speed_level < 243) then return 160/256 elseif (self.speed_level < 243) then return 160/256
elseif (self.speed_level < 247) then return 192/256 elseif (self.speed_level < 247) then return 192/256
elseif (self.speed_level < 251) then return 224/256 elseif (self.speed_level < 251) then return 224/256
elseif (self.speed_level < 300) then return 1 elseif (self.speed_level < 300) then return 1
elseif (self.speed_level < 330) then return 2 elseif (self.speed_level < 330) then return 2
elseif (self.speed_level < 360) then return 3 elseif (self.speed_level < 360) then return 3
elseif (self.speed_level < 400) then return 4 elseif (self.speed_level < 400) then return 4
elseif (self.speed_level < 420) then return 5 elseif (self.speed_level < 420) then return 5
elseif (self.speed_level < 450) then return 4 elseif (self.speed_level < 450) then return 4
elseif (self.speed_level < 500) then return 3 elseif (self.speed_level < 500) then return 3
else return 20 else return 20
end end
end end
function MarathonA3Game:advanceOneFrame() function MarathonA3Game:advanceOneFrame()
@ -146,30 +149,31 @@ end
function MarathonA3Game:onPieceEnter() function MarathonA3Game:onPieceEnter()
if (self.level % 100 ~= 99) and self.level ~= 998 and self.frames ~= 0 then if (self.level % 100 ~= 99) and self.level ~= 998 and self.frames ~= 0 then
self:updateSectionTimes(self.level, self.level + 1) self:updateSectionTimes(self.level, self.level + 1)
self.level = self.level + 1 self.level = self.level + 1
self.speed_level = self.speed_level + 1 self.speed_level = self.speed_level + 1
end self.torikan_passed = self.level >= 500 and true or false
end
end end
local cleared_row_levels = {1, 2, 4, 6} local cleared_row_levels = {1, 2, 4, 6}
function MarathonA3Game:onLineClear(cleared_row_count) function MarathonA3Game:onLineClear(cleared_row_count)
local advanced_levels = cleared_row_levels[cleared_row_count] local advanced_levels = cleared_row_levels[cleared_row_count]
self:updateSectionTimes(self.level, self.level + advanced_levels) self:updateSectionTimes(self.level, self.level + advanced_levels)
if not self.clear then if not self.clear then
self.level = math.min(self.level + advanced_levels, 999) self.level = math.min(self.level + advanced_levels, 999)
end end
self.speed_level = self.speed_level + advanced_levels self.speed_level = self.speed_level + advanced_levels
if self.level == 999 and not self.clear then if self.level == 999 and not self.clear then
self.clear = true self.clear = true
self.grid:clear() self.grid:clear()
self.roll_frames = -150 self.roll_frames = -150
end end
if self.level >= 500 and self.frames >= 25200 then if not self.torikan_passed and self.level >= 500 and self.frames >= 25200 then
self.level = 500 self.level = 500
self.game_over = true self.game_over = true
end end
end end
local cool_cutoffs = { local cool_cutoffs = {
@ -192,48 +196,53 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
table.insert(self.section_times, section_time) table.insert(self.section_times, section_time)
self.section_start_time = self.frames self.section_start_time = self.frames
self.speed_level = self.section_cool and self.speed_level + 100 or self.speed_level
if section_time > regret_cutoffs[section] then if section_time > regret_cutoffs[section] then
self.section_cool_grade = self.section_cool_grade - 1 self.section_cool_grade = self.section_cool_grade - 1
table.insert(self.section_status, "regret") table.insert(self.section_status, "regret")
self.coolregret_message = "REGRET!!" self.coolregret_message = "REGRET!!"
self.coolregret_timer = 300 self.coolregret_timer = 300
elseif section <= 9 and self.section_status[section - 1] == "cool" and elseif self.section_cool then
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then
self.section_cool_grade = self.section_cool_grade + 1 self.section_cool_grade = self.section_cool_grade + 1
self.speed_level = self.speed_level + 100
table.insert(self.section_status, "cool") table.insert(self.section_status, "cool")
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
elseif self.section_status[section - 1] == "cool" then
table.insert(self.section_status, "none")
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
self.section_cool_grade = self.section_cool_grade + 1
self.speed_level = self.speed_level + 100
table.insert(self.section_status, "cool")
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
else else
table.insert(self.section_status, "none") table.insert(self.section_status, "none")
end end
elseif section <= 9 and old_level % 100 < 70 and new_level % 100 >= 70 then
self.section_cool = false
elseif old_level % 100 < 70 and new_level % 100 >= 70 then
-- record section 70 time -- record section 70 time
section_70_time = self.frames - self.section_start_time section_70_time = self.frames - self.section_start_time
table.insert(self.section_70_times, section_70_time) table.insert(self.section_70_times, section_70_time)
if section <= 9 and self.section_status[section - 1] == "cool" and
self.section_70_times[section] < self.section_70_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.section_70_times[section] < cool_cutoffs[section] then
self.section_cool = true
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
end
end end
end end
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
self:updateGrade(cleared_lines) if not self.clear then
if cleared_lines > 0 then self:updateGrade(cleared_lines)
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -353,6 +362,8 @@ function MarathonA3Game:getLetterGrade()
return "M" .. tostring(grade - 17) return "M" .. tostring(grade - 17)
elseif grade < 32 then elseif grade < 32 then
return master_grades[grade - 26] return master_grades[grade - 26]
elseif grade >= 32 and self.roll_frames < 3238 then
return "MM"
else else
return "GM" return "GM"
end end
@ -398,9 +409,9 @@ function MarathonA3Game:drawScoringInfo()
love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
-- draw section time data -- draw section time data
current_section = math.floor(self.level / 100) + 1 current_section = math.floor(self.level / 100) + 1
@ -427,21 +438,24 @@ function MarathonA3Game:drawScoringInfo()
current_x = section_70_x current_x = section_70_x
end end
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left") if not self.clear then love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left") end
if(self.coolregret_timer > 0) then if(self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center") love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
self.coolregret_timer = self.coolregret_timer - 1 self.coolregret_timer = self.coolregret_timer - 1
end end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left") 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.clear then love.graphics.setColor(0, 1, 0, 1) end
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left") 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") love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
love.graphics.setFont(font_8x11) love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")

View File

@ -24,12 +24,13 @@ function MarathonAX4Game:new()
self.section_clear = false self.section_clear = false
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
end end
function MarathonAX4Game:getARE() function MarathonAX4Game:getARE()
if self.lines < 10 then return 18 if self.lines < 10 then return 18
elseif self.lines < 40 then return 14 elseif self.lines < 40 then return 14
elseif self.lines < 60 then return 12 elseif self.lines < 60 then return 12
elseif self.lines < 70 then return 10 elseif self.lines < 70 then return 10
@ -43,14 +44,14 @@ function MarathonAX4Game:getLineARE()
end end
function MarathonAX4Game:getDasLimit() function MarathonAX4Game:getDasLimit()
if self.lines < 20 then return 10 if self.lines < 20 then return 10
elseif self.lines < 50 then return 9 elseif self.lines < 50 then return 9
elseif self.lines < 70 then return 8 elseif self.lines < 70 then return 8
else return 7 end else return 7 end
end end
function MarathonAX4Game:getLineClearDelay() function MarathonAX4Game:getLineClearDelay()
if self.lines < 10 then return 14 if self.lines < 10 then return 14
elseif self.lines < 30 then return 9 elseif self.lines < 30 then return 9
else return 5 end else return 5 end
end end
@ -98,6 +99,7 @@ function MarathonAX4Game:onLineClear(cleared_row_count)
self:updateSectionTimes(self.lines, new_lines) self:updateSectionTimes(self.lines, new_lines)
self.lines = math.min(new_lines, 150) self.lines = math.min(new_lines, 150)
if self.lines == 150 then if self.lines == 150 then
self.grid:clear()
self.clear = true self.clear = true
self.roll_frames = -150 self.roll_frames = -150
end end

View File

@ -1,185 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Randomizer = require 'tetris.randomizers.randomizer'
local MarathonC89Game = GameMode:extend()
MarathonC89Game.name = "Marathon C89"
MarathonC89Game.hash = "MarathonC89"
MarathonC89Game.tagline = "Can you play fast enough to reach the killscreen?"
function MarathonC89Game:new()
MarathonC89Game.super:new()
self.randomizer = Randomizer()
self.ready_frames = 1
self.waiting_frames = 72
self.start_level = 12
self.level = 12
self.lock_drop = true
self.enable_hard_drop = false
self.enable_hold = false
self.next_queue_length = 1
self.additive_gravity = false
end
function MarathonC89Game:getDropSpeed() return 1/2 end
function MarathonC89Game:getDasLimit() return 16 end
function MarathonC89Game:getARR() return 6 end
function MarathonC89Game:getARE() return 6 end
function MarathonC89Game:getLineARE() return 6 end
function MarathonC89Game:getLineClearDelay() return 30 end
function MarathonC89Game:getLockDelay() return 0 end
function MarathonC89Game:chargeDAS(inputs)
if inputs[self.das.direction] == true and
self.prev_inputs[self.das.direction] == true and
not inputs["down"] and
self.piece ~= nil
then
local das_frames = self.das.frames + 1
if das_frames >= self:getDasLimit() then
if self.das.direction == "left" then
self.move = (self:getARR() == 0 and "speed" or "") .. "left"
self.das.frames = self:getDasLimit() - self:getARR()
elseif self.das.direction == "right" then
self.move = (self:getARR() == 0 and "speed" or "") .. "right"
self.das.frames = self:getDasLimit() - self:getARR()
end
else
self.move = "none"
self.das.frames = das_frames
end
elseif inputs["right"] == true then
self.das.direction = "right"
if not inputs["down"] and self.piece ~= nil then
self.move = "right"
self.das.frames = 0
else
self.move = "none"
end
elseif inputs["left"] == true then
self.das.direction = "left"
if not inputs["down"] and self.piece ~= nil then
self.move = "left"
self.das.frames = 0
else
self.move = "none"
end
else
self.move = "none"
end
if self.das.direction == "left" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=-1, y=0}) or
self.das.direction == "right" and self.piece ~= nil and self.piece:isMoveBlocked(self.grid, {x=1, y=0})
then
self.das.frames = self:getDasLimit()
end
if inputs["down"] == false and self.prev_inputs["down"] == true then
self.drop_bonus = 0
end
end
local gravity_table = {
[0] =
1366/65536, 1525/65536, 1725/65536, 1986/65536, 2341/65536,
2850/65536, 3641/65536, 5042/65536, 8192/65536, 10923/65536,
13108/65536, 13108/65536, 13108/65536, 16384/65536, 16384/65536,
16384/65536, 21846/65536, 21846/65536, 21846/65536
}
function MarathonC89Game:getGravity()
if self.waiting_frames > 0 then return 0 end
if self.level >= 29 then return 1
elseif self.level >= 19 then return 1/2
else return gravity_table[self.level] end
end
function MarathonC89Game:advanceOneFrame()
if self.waiting_frames > 0 then
self.waiting_frames = self.waiting_frames - 1
else
self.frames = self.frames + 1
end
return true
end
function MarathonC89Game:onPieceLock()
self.score = self.score + self.drop_bonus
self.drop_bonus = 0
end
local cleared_line_scores = { 40, 100, 300, 1200 }
function MarathonC89Game:getLevelForLines()
if self.start_level < 10 then
return math.max(self.start_level, math.floor(self.lines / 10))
elseif self.start_level < 16 then
return math.max(self.start_level, self.start_level + math.floor((self.lines - 100) / 10))
else
return math.max(self.start_level, math.floor((self.lines - 60) / 10))
end
end
function MarathonC89Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then
self.score = self.score + cleared_line_scores[cleared_lines] * (self.level + 1)
self.lines = self.lines + cleared_lines
self.level = self:getLevelForLines()
else
self.drop_bonus = 0
self.combo = 1
end
end
function MarathonC89Game:drawGrid()
self.grid:draw()
if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
end
end
function MarathonC89Game:drawScoringInfo()
MarathonC89Game.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("LINES", 240, 120, 40, "left")
love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.lines, 240, 140, 90, "left")
love.graphics.printf(self.score, 240, 220, 90, "left")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
end
function MarathonC89Game:getBackground()
return math.min(self.level, 19)
end
function MarathonC89Game:getHighscoreData()
return {
score = self.score,
level = self.level,
}
end
return MarathonC89Game

View File

@ -1,170 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
local PacerTest = GameMode:extend()
PacerTest.name = "TetrisGram™ Pacer Test"
PacerTest.hash = "PacerTest"
PacerTest.tagline = ""
local function getLevelFrames(level)
if level == 1 then return 72 * 60 / 8.0
else return 72 * 60 / (8 + level * 0.5)
end
end
local level_end_sections = {
7, 15, 23, 32, 41, 51, 61, 72, 83, 94,
106, 118, 131, 144, 157, 171, 185, 200,
215, 231, 247
}
function PacerTest:new()
PacerTest.super:new()
self.ready_frames = 2430
self.clear_frames = 0
self.randomizer = History6RollsRandomizer()
self.level = 1
self.section = 0
self.level_frames = 0
self.section_lines = 0
self.section_clear = false
self.strikes = 0
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.instant_hard_drop = true
self.instant_soft_drop = false
self.next_queue_length = 3
end
function PacerTest:initialize(ruleset)
for i = 1, 30 do
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
self.level_frames = getLevelFrames(1)
switchBGM("pacer_test")
end
function PacerTest:getARE()
return 0
end
function PacerTest:getLineARE()
return 0
end
function PacerTest:getDasLimit()
return 8
end
function PacerTest:getLineClearDelay()
return 6
end
function PacerTest:getLockDelay()
return 30
end
function PacerTest:getGravity()
return 1/64
end
function PacerTest:getSection()
return math.floor(level / 100) + 1
end
function PacerTest:advanceOneFrame()
if self.clear then
self.clear_frames = self.clear_frames + 1
if self.clear_frames > 600 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
self.level_frames = self.level_frames - 1
if self.level_frames <= 0 then
self:checkSectionStatus()
self.section = self.section + 1
if self.section >= level_end_sections[self.level] then
self.level = self.level + 1
end
self.level_frames = self.level_frames + getLevelFrames(self.level)
end
end
return true
end
function PacerTest:checkSectionStatus()
if self.section_clear then
self.strikes = 0
self.section_clear = false
else
self.strikes = self.strikes + 1
if self.strikes >= 2 then
self.game_over = true
fadeoutBGM(2.5)
end
end
self.section_lines = 0
end
function PacerTest:onLineClear(cleared_row_count)
self.section_lines = self.section_lines + cleared_row_count
if self.section_lines >= 3 then
self.section_clear = true
end
end
function PacerTest:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function PacerTest:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function PacerTest:drawScoringInfo()
PacerTest.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("LINES", text_x, 224, 70, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
for i = 1, math.min(self.strikes, 3) do
love.graphics.draw(misc_graphics["strike"], text_x + (i - 1) * 30, 280)
end
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.section_lines .. "/3", text_x, 244, 40, "left")
love.graphics.printf(self.level, text_x, 340, 40, "right")
love.graphics.printf(self.section, text_x, 370, 40, "right")
end
function PacerTest:getBackground()
return math.min(self.level - 1, 19)
end
return PacerTest

View File

@ -15,13 +15,14 @@ function PhantomManiaGame:new()
PhantomManiaGame.super:new() PhantomManiaGame.super:new()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.next_queue_length = 1 self.next_queue_length = 1
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
@ -29,7 +30,7 @@ function PhantomManiaGame:new()
end end
function PhantomManiaGame:getARE() function PhantomManiaGame:getARE()
if self.level < 100 then return 18 if self.level < 100 then return 18
elseif self.level < 200 then return 14 elseif self.level < 200 then return 14
elseif self.level < 400 then return 8 elseif self.level < 400 then return 8
elseif self.level < 500 then return 7 elseif self.level < 500 then return 7
@ -37,14 +38,14 @@ function PhantomManiaGame:getARE()
end end
function PhantomManiaGame:getLineARE() function PhantomManiaGame:getLineARE()
if self.level < 100 then return 18 if self.level < 100 then return 18
elseif self.level < 400 then return 8 elseif self.level < 400 then return 8
elseif self.level < 500 then return 7 elseif self.level < 500 then return 7
else return 6 end else return 6 end
end end
function PhantomManiaGame:getDasLimit() function PhantomManiaGame:getDasLimit()
if self.level < 200 then return 11 if self.level < 200 then return 11
elseif self.level < 300 then return 10 elseif self.level < 300 then return 10
elseif self.level < 400 then return 9 elseif self.level < 400 then return 9
else return 7 end else return 7 end
@ -55,7 +56,7 @@ function PhantomManiaGame:getLineClearDelay()
end end
function PhantomManiaGame:getLockDelay() function PhantomManiaGame:getLockDelay()
if self.level < 100 then return 30 if self.level < 100 then return 30
elseif self.level < 200 then return 26 elseif self.level < 200 then return 26
elseif self.level < 300 then return 22 elseif self.level < 300 then return 22
elseif self.level < 400 then return 18 elseif self.level < 400 then return 18
@ -117,16 +118,17 @@ function PhantomManiaGame:onLineClear(cleared_row_count)
end end
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines) function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then if not self.clear then
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -170,10 +172,10 @@ function PhantomManiaGame:drawScoringInfo()
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("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
love.graphics.setFont(font_3x5_3) 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 love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end
@ -185,9 +187,9 @@ function PhantomManiaGame:drawScoringInfo()
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
end end
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
end end
function PhantomManiaGame:getSectionEndLevel() function PhantomManiaGame:getSectionEndLevel()

View File

@ -16,7 +16,6 @@ PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make
function PhantomMania2Game:new() function PhantomMania2Game:new()
PhantomMania2Game.super:new() PhantomMania2Game.super:new()
self.level = 0
self.grade = 0 self.grade = 0
self.garbage = 0 self.garbage = 0
self.clear = false self.clear = false
@ -27,26 +26,30 @@ function PhantomMania2Game:new()
self.queue_age = 0 self.queue_age = 0
self.roll_points = 0 self.roll_points = 0
self.SGnames = { self.SGnames = {
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "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" "GM"
} }
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
self.coolregret_message = ""
self.coolregret_timer = 0
end end
function PhantomMania2Game:getARE() function PhantomMania2Game:getARE()
if self.level < 300 then return 12 if self.level < 300 then return 12
else return 6 end else return 6 end
end end
function PhantomMania2Game:getLineARE() function PhantomMania2Game:getLineARE()
if self.level < 100 then return 8 if self.level < 100 then return 8
elseif self.level < 200 then return 7 elseif self.level < 200 then return 7
elseif self.level < 500 then return 6 elseif self.level < 500 then return 6
elseif self.level < 1300 then return 5 elseif self.level < 1300 then return 5
@ -54,7 +57,7 @@ function PhantomMania2Game:getLineARE()
end end
function PhantomMania2Game:getDasLimit() function PhantomMania2Game:getDasLimit()
if self.level < 200 then return 9 if self.level < 200 then return 9
elseif self.level < 500 then return 7 elseif self.level < 500 then return 7
else return 5 end else return 5 end
end end
@ -64,7 +67,7 @@ function PhantomMania2Game:getLineClearDelay()
end end
function PhantomMania2Game:getLockDelay() function PhantomMania2Game:getLockDelay()
if self.level < 200 then return 18 if self.level < 200 then return 18
elseif self.level < 300 then return 17 elseif self.level < 300 then return 17
elseif self.level < 500 then return 15 elseif self.level < 500 then return 15
elseif self.level < 600 then return 13 elseif self.level < 600 then return 13
@ -122,6 +125,8 @@ function PhantomMania2Game:advanceOneFrame()
return false return false
elseif self.roll_frames > 3238 then elseif self.roll_frames > 3238 then
switchBGM(nil) switchBGM(nil)
self.roll_points = self.level >= 1300 and self.roll_points + 150 or self.roll_points
self.grade = self.grade + math.floor(self.roll_points / 100)
self.completed = true self.completed = true
end end
elseif self.ready_frames == 0 then elseif self.ready_frames == 0 then
@ -143,7 +148,8 @@ function PhantomMania2Game:onPieceEnter()
end end
local cleared_row_levels = {1, 2, 4, 6} local cleared_row_levels = {1, 2, 4, 6}
local cleared_row_points = {2, 6, 15, 40} local torikan_roll_points = {10, 20, 30, 100}
local big_roll_points = {10, 20, 100, 200}
function PhantomMania2Game:onLineClear(cleared_row_count) function PhantomMania2Game:onLineClear(cleared_row_count)
if not self.clear then if not self.clear then
@ -162,7 +168,8 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
end end
self:advanceBottomRow(-cleared_row_count) self:advanceBottomRow(-cleared_row_count)
else else
self.roll_points = self.roll_points + cleared_row_points[cleared_row_count / 2] if self.big_mode then self.roll_points = self.roll_points + big_roll_points[cleared_row_count / 2]
else self.roll_points = self.roll_points + torikan_roll_points[cleared_row_count] end
if self.roll_points >= 100 then if self.roll_points >= 100 then
self.roll_points = self.roll_points - 100 self.roll_points = self.roll_points - 100
self.grade = self.grade + 1 self.grade = self.grade + 1
@ -171,24 +178,27 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
end end
function PhantomMania2Game:onPieceLock(piece, cleared_row_count) function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
self.super:onPieceLock()
if cleared_row_count == 0 then self:advanceBottomRow(1) end if cleared_row_count == 0 then self:advanceBottomRow(1) end
end end
function PhantomMania2Game:onHold() function PhantomMania2Game:onHold()
self.super.onHold()
self.hold_age = 0 self.hold_age = 0
end end
function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines) function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then if not self.clear then
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -213,8 +223,13 @@ function PhantomMania2Game:updateSectionTimes(old_level, new_level)
self.section_start_time = self.frames self.section_start_time = self.frames
if section_time <= cool_cutoffs[section] then if section_time <= cool_cutoffs[section] then
self.grade = self.grade + 2 self.grade = self.grade + 2
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
elseif section_time <= regret_cutoffs[section] then elseif section_time <= regret_cutoffs[section] then
self.grade = self.grade + 1 self.grade = self.grade + 1
else
self.coolregret_message = "REGRET!!"
self.coolregret_timer = 300
end end
end end
end end
@ -291,10 +306,17 @@ function PhantomMania2Game:drawScoringInfo()
love.graphics.printf("GRADE", text_x, 120, 40, "left") love.graphics.printf("GRADE", text_x, 120, 40, "left")
love.graphics.printf("SCORE", text_x, 200, 40, "left") love.graphics.printf("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
self:drawSectionTimesWithSplits(math.floor(self.level / 100) + 1)
if(self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
self.coolregret_timer = self.coolregret_timer - 1
end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left") love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
@ -306,9 +328,9 @@ function PhantomMania2Game:drawScoringInfo()
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right") love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
end end
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
end end
function PhantomMania2Game:getBackground() function PhantomMania2Game:getBackground()

View File

@ -1,22 +0,0 @@
local PhantomManiaGame = require 'tetris.modes.phantom_mania'
local PhantomManiaNGame = PhantomManiaGame:extend()
PhantomManiaNGame.name = "Phantom Mania N"
PhantomManiaNGame.hash = "PhantomManiaN"
PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS support."
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
return PhantomManiaNGame

View File

@ -1,129 +0,0 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
local Race40Game = GameMode:extend()
Race40Game.name = "Race 40"
Race40Game.hash = "Race40"
Race40Game.tagline = "How fast can you clear 40 lines?"
function Race40Game:new()
Race40Game.super:new()
self.lines = 0
self.line_goal = 40
self.pieces = 0
self.randomizer = Bag7Randomiser()
self.roll_frames = 0
self.lock_drop = true
self.lock_hard_drop = true
self.instant_hard_drop = true
self.instant_soft_drop = false
self.enable_hold = true
self.next_queue_length = 3
end
function Race40Game:getDropSpeed()
return 20
end
function Race40Game:getARR()
return 0
end
function Race40Game:getARE()
return 0
end
function Race40Game:getLineARE()
return self:getARE()
end
function Race40Game:getDasLimit()
return 6
end
function Race40Game:getLineClearDelay()
return 0
end
function Race40Game:getLockDelay()
return 15
end
function Race40Game:getGravity()
return 1/64
end
function Race40Game:advanceOneFrame()
if self.clear then
self.roll_frames = self.roll_frames + 1
if self.roll_frames > 150 then
self.completed = true
end
return false
elseif self.ready_frames == 0 then
self.frames = self.frames + 1
end
return true
end
function Race40Game:onPieceLock()
self.pieces = self.pieces + 1
end
function Race40Game:onLineClear(cleared_row_count)
if not self.clear then
self.lines = self.lines + cleared_row_count
if self.lines >= self.line_goal then
self.clear = true
end
end
end
function Race40Game:drawGrid(ruleset)
self.grid:draw()
if self.piece ~= nil then
self:drawGhostPiece(ruleset)
end
end
function Race40Game:getHighscoreData()
return {
level = self.level,
frames = self.frames,
}
end
function Race40Game:drawScoringInfo()
Race40Game.super.drawScoringInfo(self)
love.graphics.setColor(1, 1, 1, 1)
local text_x = config["side_next"] and 320 or 240
love.graphics.setFont(font_3x5_2)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("LINES", text_x, 320, 40, "left")
love.graphics.printf("line/min", text_x, 160, 80, "left")
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
love.graphics.setFont(font_3x5_3)
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
love.graphics.setFont(font_3x5_4)
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
end
function Race40Game:getBackground()
return 2
end
return Race40Game

View File

@ -16,7 +16,6 @@ StrategyGame.tagline = "You have lots of time to think! Can you use it to place
function StrategyGame:new() function StrategyGame:new()
StrategyGame.super:new() StrategyGame.super:new()
self.level = 0
self.clear = false self.clear = false
self.completed = false self.completed = false
self.roll_frames = 0 self.roll_frames = 0
@ -28,7 +27,7 @@ function StrategyGame:new()
end end
function StrategyGame:getARE() function StrategyGame:getARE()
if self.level < 100 then return 60 if self.level < 100 then return 60
elseif self.level < 200 then return 54 elseif self.level < 200 then return 54
elseif self.level < 300 then return 48 elseif self.level < 300 then return 48
elseif self.level < 400 then return 42 elseif self.level < 400 then return 42
@ -53,7 +52,7 @@ function StrategyGame:getLineClearDelay()
end end
function StrategyGame:getLockDelay() function StrategyGame:getLockDelay()
if self.level < 500 then return 8 if self.level < 500 then return 8
elseif self.level < 700 then return 6 elseif self.level < 700 then return 6
else return 4 end else return 4 end
end end
@ -84,7 +83,7 @@ function StrategyGame:advanceOneFrame()
end end
function StrategyGame:onPieceEnter() function StrategyGame:onPieceEnter()
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1 self.level = self.level + 1
end end
end end
@ -99,16 +98,17 @@ function StrategyGame:onLineClear(cleared_row_count)
end end
function StrategyGame:updateScore(level, drop_bonus, cleared_lines) function StrategyGame:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then if not self.clear then
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -135,11 +135,11 @@ function StrategyGame:drawScoringInfo()
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, text_x, 220, 90, "left") love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 50, "right") love.graphics.printf(self.level, text_x, 340, 40, "right")
if self.clear then if self.clear then
love.graphics.printf(self.level, text_x, 370, 50, "right") love.graphics.printf(self.level, text_x, 370, 40, "right")
else else
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right") love.graphics.printf(self.level < 900 and math.floor(self.level / 100 + 1) * 100 or 999, text_x, 370, 40, "right")
end end
end end

View File

@ -27,6 +27,7 @@ function Survival2020Game:new()
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
end end
@ -45,7 +46,7 @@ function Survival2020Game:getLineARE()
end end
function Survival2020Game:getDasLimit() function Survival2020Game:getDasLimit()
if self.level < 200 then return 9 if self.level < 200 then return 9
elseif self.level < 500 then return 7 elseif self.level < 500 then return 7
elseif self.level < 1000 then return 5 elseif self.level < 1000 then return 5
elseif self.level < 1500 then return 4 elseif self.level < 1500 then return 4
@ -174,16 +175,17 @@ function Survival2020Game:onLineClear(cleared_row_count)
end end
function Survival2020Game:updateScore(level, drop_bonus, cleared_lines) function Survival2020Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then if not self.clear then
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end

View File

@ -15,28 +15,28 @@ SurvivalA1Game.tagline = "The game starts fast and only gets faster!"
function SurvivalA1Game:new() function SurvivalA1Game:new()
SurvivalA1Game.super:new() SurvivalA1Game.super:new()
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.bravos = 0 self.bravos = 0
self.gm_conditions = { self.gm_conditions = {
level300 = false, level300 = false,
level500 = false, level500 = false,
level999 = false level999 = false
} }
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.randomizer = History4RollsRandomizer() self.randomizer = History4RollsRandomizer()
self.lock_drop = false self.lock_drop = false
self.enable_hard_drop = false self.enable_hard_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
@ -62,29 +62,29 @@ function SurvivalA1Game:getLockDelay()
end end
function SurvivalA1Game:getGravity() function SurvivalA1Game:getGravity()
return 20 return 20
end end
local function getRankForScore(score) local function getRankForScore(score)
if score < 400 then return {rank = "9", next = 400} if score < 400 then return {rank = "9", next = 400}
elseif score < 800 then return {rank = "8", next = 800} elseif score < 800 then return {rank = "8", next = 800}
elseif score < 1400 then return {rank = "7", next = 1400} elseif score < 1400 then return {rank = "7", next = 1400}
elseif score < 2000 then return {rank = "6", next = 2000} elseif score < 2000 then return {rank = "6", next = 2000}
elseif score < 3500 then return {rank = "5", next = 3500} elseif score < 3500 then return {rank = "5", next = 3500}
elseif score < 5500 then return {rank = "4", next = 5500} elseif score < 5500 then return {rank = "4", next = 5500}
elseif score < 8000 then return {rank = "3", next = 8000} elseif score < 8000 then return {rank = "3", next = 8000}
elseif score < 12000 then return {rank = "2", next = 12000} elseif score < 12000 then return {rank = "2", next = 12000}
elseif score < 16000 then return {rank = "1", next = 16000} elseif score < 16000 then return {rank = "1", next = 16000}
elseif score < 22000 then return {rank = "S1", next = 22000} elseif score < 22000 then return {rank = "S1", next = 22000}
elseif score < 30000 then return {rank = "S2", next = 30000} elseif score < 30000 then return {rank = "S2", next = 30000}
elseif score < 40000 then return {rank = "S3", next = 40000} elseif score < 40000 then return {rank = "S3", next = 40000}
elseif score < 52000 then return {rank = "S4", next = 52000} elseif score < 52000 then return {rank = "S4", next = 52000}
elseif score < 66000 then return {rank = "S5", next = 66000} elseif score < 66000 then return {rank = "S5", next = 66000}
elseif score < 82000 then return {rank = "S6", next = 82000} elseif score < 82000 then return {rank = "S6", next = 82000}
elseif score < 100000 then return {rank = "S7", next = 100000} elseif score < 100000 then return {rank = "S7", next = 100000}
elseif score < 120000 then return {rank = "S8", next = 120000} elseif score < 120000 then return {rank = "S8", next = 120000}
else return {rank = "S9", next = "???"} else return {rank = "S9", next = "???"}
end end
end end
function SurvivalA1Game:advanceOneFrame() function SurvivalA1Game:advanceOneFrame()
@ -106,47 +106,48 @@ function SurvivalA1Game:onPieceEnter()
end end
function SurvivalA1Game:onLineClear(cleared_row_count) function SurvivalA1Game:onLineClear(cleared_row_count)
self:checkGMRequirements(self.level, self.level + cleared_row_count) self:checkGMRequirements(self.level, self.level + cleared_row_count)
if not self.clear then if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999) local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 then if new_level == 999 then
self.clear = true self.clear = true
else else
self.level = new_level self.level = new_level
end end
end end
end end
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines) function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
if self.grid:checkForBravo(cleared_lines) then if not self.clear then
self.bravo = 4 if self.grid:checkForBravo(cleared_lines) then
self.bravos = self.bravos + 1 self.bravo = 4
else self.bravo = 1 end self.bravos = self.bravos + 1
if cleared_lines > 0 then else self.bravo = 1 end
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * self.bravo * self.combo self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo * self.bravo
self.combo = self.combo + (cleared_lines - 1) * 2 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
function SurvivalA1Game:checkGMRequirements(old_level, new_level) function SurvivalA1Game:checkGMRequirements(old_level, new_level)
if old_level < 300 and new_level >= 300 then if old_level < 300 and new_level >= 300 then
if self.score > 12000 and self.frames <= frameTime(4,15) then if self.score >= 12000 and self.frames <= frameTime(4,15) then
self.gm_conditions["level300"] = true self.gm_conditions["level300"] = true
end end
elseif old_level < 500 and new_level >= 500 then elseif old_level < 500 and new_level >= 500 then
if self.score > 40000 and self.frames <= frameTime(7,30) then if self.score >= 40000 and self.frames <= frameTime(7,30) then
self.gm_conditions["level500"] = true self.gm_conditions["level500"] = true
end end
elseif old_level < 999 and new_level >= 999 then elseif old_level < 999 and new_level >= 999 then
if self.score > 126000 and self.frames <= frameTime(13,30) then if self.score >= 126000 and self.frames <= frameTime(13,30) then
self.gm_conditions["level900"] = true self.gm_conditions["level999"] = true
end end
end end
end end
@ -170,16 +171,16 @@ function SurvivalA1Game:drawScoringInfo()
love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("SCORE", 240, 200, 40, "left")
love.graphics.printf("NEXT RANK", 240, 260, 90, "left") love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.score, 240, 220, 90, "left")
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
love.graphics.printf("GM", 240, 140, 90, "left") love.graphics.printf("GM", 240, 140, 90, "left")
else else
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left") love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
@ -187,9 +188,9 @@ function SurvivalA1Game:drawScoringInfo()
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left") love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end
love.graphics.setFont(font_8x11) love.graphics.setFont(font_8x11)

View File

@ -20,13 +20,14 @@ function SurvivalA2Game:new()
self.combo = 1 self.combo = 1
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"GM" "GM"
} }
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
end end
function SurvivalA2Game:getARE() function SurvivalA2Game:getARE()
@ -88,7 +89,7 @@ function SurvivalA2Game:advanceOneFrame()
end end
function SurvivalA2Game:onPieceEnter() function SurvivalA2Game:onPieceEnter()
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
self.level = self.level + 1 self.level = self.level + 1
end end
end end
@ -98,9 +99,9 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
local new_level = math.min(self.level + cleared_row_count, 999) local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 or self:hitTorikan(self.level, new_level) then if self.level == 999 or self:hitTorikan(self.level, new_level) then
self.clear = true self.clear = true
if self.level < 999 then if self.level < 999 then
self.game_over = true self.game_over = true
end end
else else
self.level = new_level self.level = new_level
end end
@ -108,17 +109,18 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
end end
function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines) function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines)
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end if not self.clear then
if cleared_lines > 0 then if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * self.bravo * self.combo self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo * self.bravo
self.combo = self.combo + (cleared_lines - 1) * 2 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -149,19 +151,21 @@ function SurvivalA2Game:drawScoringInfo()
if self:getLetterGrade() ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end if self:getLetterGrade() ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
love.graphics.printf("SCORE", text_x, 200, 40, "left") love.graphics.printf("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, text_x, 220, 90, "left") love.graphics.printf(self.score, text_x, 220, 90, "left")
if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1)
elseif 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 if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
love.graphics.printf(self.level, text_x, 340, 40, "right") love.graphics.printf(self.level, text_x, 340, 40, "right")
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right") love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
end end
function SurvivalA2Game:getSectionEndLevel() function SurvivalA2Game:getSectionEndLevel()

View File

@ -12,11 +12,8 @@ SurvivalA3Game.hash = "SurvivalA3"
SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?" SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?"
function SurvivalA3Game:new() function SurvivalA3Game:new()
SurvivalA3Game.super:new() SurvivalA3Game.super:new()
self.level = 0
self.grade = 0 self.grade = 0
self.garbage = 0 self.garbage = 0
self.clear = false self.clear = false
@ -25,13 +22,14 @@ function SurvivalA3Game:new()
self.combo = 1 self.combo = 1
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.SGnames = { self.SGnames = {
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "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" "GM"
} }
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
@ -41,19 +39,19 @@ end
function SurvivalA3Game:initialize(ruleset) function SurvivalA3Game:initialize(ruleset)
self.torikan_time = frameTime(2,28) self.torikan_time = frameTime(2,28)
if ruleset.world then self.torikan_time = frameTime(3,03) end if ruleset.world then self.torikan_time = frameTime(3,03) end
self.super.initialize(self, ruleset) self.super.initialize(self, ruleset)
-- ^ notice the . here instead of the : -- ^ notice the . here instead of the :
end end
function SurvivalA3Game:getARE() function SurvivalA3Game:getARE()
if self.level < 300 then return 12 if self.level < 300 then return 12
else return 6 end else return 6 end
end end
function SurvivalA3Game:getLineARE() function SurvivalA3Game:getLineARE()
if self.level < 100 then return 8 if self.level < 100 then return 8
elseif self.level < 200 then return 7 elseif self.level < 200 then return 7
elseif self.level < 500 then return 6 elseif self.level < 500 then return 6
elseif self.level < 1300 then return 5 elseif self.level < 1300 then return 5
@ -61,7 +59,7 @@ function SurvivalA3Game:getLineARE()
end end
function SurvivalA3Game:getDasLimit() function SurvivalA3Game:getDasLimit()
if self.level < 100 then return 9 if self.level < 100 then return 9
elseif self.level < 500 then return 7 elseif self.level < 500 then return 7
else return 5 end else return 5 end
end end
@ -72,7 +70,7 @@ function SurvivalA3Game:getLineClearDelay()
end end
function SurvivalA3Game:getLockDelay() function SurvivalA3Game:getLockDelay()
if self.level < 200 then return 18 if self.level < 200 then return 18
elseif self.level < 300 then return 17 elseif self.level < 300 then return 17
elseif self.level < 500 then return 15 elseif self.level < 500 then return 15
elseif self.level < 600 then return 13 elseif self.level < 600 then return 13
@ -140,21 +138,20 @@ function SurvivalA3Game:onPieceEnter()
end end
local cleared_row_levels = {1, 2, 4, 6} local cleared_row_levels = {1, 2, 4, 6}
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
function SurvivalA3Game:onLineClear(cleared_row_count) function SurvivalA3Game:onLineClear(cleared_row_count)
if not self.clear then if not self.clear then
local new_level = self.level + cleared_row_levels[cleared_row_count] local new_level = self.level + cleared_row_levels[cleared_row_count]
self:updateSectionTimes(self.level, new_level) self:updateSectionTimes(self.level, new_level)
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
self.clear = true self.clear = true
if new_level >= 1300 then if new_level >= 1300 then
self.level = 1300 self.level = 1300
self.grid:clear() self.grid:clear()
self.big_mode = true self.big_mode = true
self.roll_frames = -150 self.roll_frames = -150
else else
self.game_over = true self.game_over = true
end end
else else
self.level = math.min(new_level, 1300) self.level = math.min(new_level, 1300)
@ -164,20 +161,22 @@ function SurvivalA3Game:onLineClear(cleared_row_count)
end end
function SurvivalA3Game:onPieceLock(piece, cleared_row_count) function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
self.super:onPieceLock()
if cleared_row_count == 0 then self:advanceBottomRow(1) end if cleared_row_count == 0 then self:advanceBottomRow(1) end
end end
function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines) function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines)
if cleared_lines > 0 then if not self.clear then
self.score = self.score + ( if cleared_lines > 0 then
(math.ceil((level + cleared_lines) / 4) + drop_bonus) * self.combo = self.combo + (cleared_lines - 1) * 2
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1) self.score = self.score + (
) (math.ceil((level + cleared_lines) / 4) + drop_bonus) *
self.lines = self.lines + cleared_lines cleared_lines * self.combo
self.combo = self.combo + cleared_lines - 1 )
else else
self.combo = 1
end
self.drop_bonus = 0 self.drop_bonus = 0
self.combo = 1
end end
end end
@ -229,10 +228,10 @@ function SurvivalA3Game:drawScoringInfo()
love.graphics.printf("GRADE", text_x, 120, 40, "left") love.graphics.printf("GRADE", text_x, 120, 40, "left")
love.graphics.printf("SCORE", text_x, 200, 40, "left") love.graphics.printf("SCORE", text_x, 200, 40, "left")
love.graphics.printf("LEVEL", text_x, 320, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left")
local sg = self.grid:checkSecretGrade() local sg = self.grid:checkSecretGrade()
if sg >= 5 then if sg >= 5 then
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
end end
if(self.coolregret_timer > 0) then if(self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center") love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
@ -243,7 +242,10 @@ function SurvivalA3Game:drawScoringInfo()
self:drawSectionTimesWithSplits(current_section) self:drawSectionTimesWithSplits(current_section)
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left") 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") love.graphics.printf(self.score, text_x, 220, 90, "left")
love.graphics.printf(self.level, text_x, 340, 50, "right") love.graphics.printf(self.level, text_x, 340, 50, "right")
if self.clear then if self.clear then
@ -251,9 +253,9 @@ function SurvivalA3Game:drawScoringInfo()
else else
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right") love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
end end
if sg >= 5 then if sg >= 5 then
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
end end
end end
function SurvivalA3Game:getBackground() function SurvivalA3Game:getBackground()

View File

@ -1,17 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag5Randomizer = Randomizer:extend()
function Bag5Randomizer:initialize()
self.bag = {"I", "J", "L", "O", "T"}
end
function Bag5Randomizer:generatePiece()
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x)
end
return Bag5Randomizer

View File

@ -1,24 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag5AltRandomizer = Randomizer:extend()
function Bag5AltRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "T"}
self.prev = nil
end
function Bag5AltRandomizer:generatePiece()
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(table.getn(self.bag))
local temp = table.remove(self.bag, x)
if temp == self.prev then
local y = math.random(table.getn(self.bag))
temp = table.remove(self.bag, y)
end
self.prev = temp
return temp
end
return Bag5AltRandomizer

View File

@ -3,27 +3,27 @@ local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7NoSZOStartRandomizer = Randomizer:extend() local Bag7NoSZOStartRandomizer = Randomizer:extend()
function Bag7NoSZOStartRandomizer:shuffleBag() function Bag7NoSZOStartRandomizer:shuffleBag()
local b = self.bag local b = self.bag
local ln = #b local ln = #b
for i = 1, ln do for i = 1, ln do
local j = math.random(i, ln) local j = math.random(i, ln)
b[i], b[j] = b[j], b[i] b[i], b[j] = b[j], b[i]
end end
end end
local function isnotSZO(x) return not(x == "S" or x == "Z" or x == "O") end local function isnotSZO(x) return not(x == "S" or x == "Z" or x == "O") end
function Bag7NoSZOStartRandomizer:initialize() function Bag7NoSZOStartRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"} self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
repeat repeat
self:shuffleBag() self:shuffleBag()
until isnotSZO(self.bag[7]) until isnotSZO(self.bag[7])
end end
function Bag7NoSZOStartRandomizer:generatePiece() function Bag7NoSZOStartRandomizer:generatePiece()
if #self.bag == 0 then if #self.bag == 0 then
self.bag = {"I", "J", "L", "O", "S", "T", "Z"} self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
self:shuffleBag() self:shuffleBag()
end end
return table.remove(self.bag) return table.remove(self.bag)
end end

View File

@ -1,24 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7Randomizer = Randomizer:extend()
function Bag7Randomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
end
function Bag7Randomizer:generatePiece()
if next(self.extra) == nil then
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
end
if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
end
local x = math.random(table.getn(self.bag))
--print("Bag: "..table.concat(self.bag, ", ").." | Extra: "..table.concat(self.extra, ", "))
return table.remove(self.bag, x)
end
return Bag7Randomizer

View File

@ -1,28 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local BagKonoha = Randomizer:extend()
function BagKonoha:initialize()
self.bag = {"I", "J", "L", "O", "T"}
self.prev = nil
self.allowrepeat = false
self.generated = 0
end
function BagKonoha:generatePiece()
self.generated = self.generated + 1
if #self.bag == 0 then
self.bag = {"I", "J", "L", "O", "T"}
end
local x = math.random(#self.bag)
local temp = table.remove(self.bag, x)
if temp == self.prev and not self.allowrepeat then
local y = math.random(#self.bag)
table.insert(self.bag, temp) -- should insert at the end of the bag, bag[y] doesnt change
temp = table.remove(self.bag, y)
end
self.prev = temp
return temp
end
return BagKonoha

View File

@ -4,22 +4,22 @@ local History4RollsRandomizer = Randomizer:extend()
function History4RollsRandomizer:initialize() function History4RollsRandomizer:initialize()
self.history = {"Z", "Z", "Z", "Z"} self.history = {"Z", "Z", "Z", "Z"}
self.first = true self.first = true
end end
function History4RollsRandomizer:generatePiece() function History4RollsRandomizer:generatePiece()
if self.first then if self.first then
self.first = false self.first = false
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
else else
local shapes = {"I", "J", "L", "O", "S", "T", "Z"} local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
for i = 1, 4 do for i = 1, 4 do
local x = math.random(7) local x = math.random(7)
if not inHistory(shapes[x], self.history) or i == 4 then if not inHistory(shapes[x], self.history) or i == 4 then
return self:updateHistory(shapes[x]) return self:updateHistory(shapes[x])
end end
end end
end end
end end
function History4RollsRandomizer:updateHistory(shape) function History4RollsRandomizer:updateHistory(shape)

View File

@ -4,22 +4,22 @@ local History6RollsRandomizer = Randomizer:extend()
function History6RollsRandomizer:initialize() function History6RollsRandomizer:initialize()
self.history = {"Z", "S", "Z", "S"} self.history = {"Z", "S", "Z", "S"}
self.first = true self.first = true
end end
function History6RollsRandomizer:generatePiece() function History6RollsRandomizer:generatePiece()
if self.first then if self.first then
self.first = false self.first = false
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
else else
local shapes = {"I", "J", "L", "O", "S", "T", "Z"} local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
for i = 1, 6 do for i = 1, 6 do
local x = math.random(7) local x = math.random(7)
if not inHistory(shapes[x], self.history) or i == 6 then if not inHistory(shapes[x], self.history) or i == 6 then
return self:updateHistory(shapes[x]) return self:updateHistory(shapes[x])
end end
end end
end end
end end
function History6RollsRandomizer:updateHistory(shape) function History6RollsRandomizer:updateHistory(shape)

View File

@ -3,67 +3,67 @@ local Randomizer = require 'tetris.randomizers.randomizer'
local History6Rolls35PoolRandomizer = Randomizer:extend() local History6Rolls35PoolRandomizer = Randomizer:extend()
function History6Rolls35PoolRandomizer:initialize() function History6Rolls35PoolRandomizer:initialize()
self.first = true self.first = true
self.history = {"Z", "S", "Z", "S"} self.history = {"Z", "S", "Z", "S"}
self.pool = { self.pool = {
"I", "I", "I", "I", "I", "I", "I", "I", "I", "I",
"T", "T", "T", "T", "T", "T", "T", "T", "T", "T",
"L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
"J", "J", "J", "J", "J", "J", "J", "J", "J", "J",
"S", "S", "S", "S", "S", "S", "S", "S", "S", "S",
"Z", "Z", "Z", "Z", "Z", "Z", "Z", "Z", "Z", "Z",
"O", "O", "O", "O", "O", "O", "O", "O", "O", "O",
}
self.droughts = {
I = 0,
T = 0,
L = 0,
J = 0,
S = 0,
Z = 0,
O = 0,
} }
self.droughts = {
I = 0,
T = 0,
L = 0,
J = 0,
S = 0,
Z = 0,
O = 0,
}
end end
function History6Rolls35PoolRandomizer:generatePiece() function History6Rolls35PoolRandomizer:generatePiece()
local index, x local index, x
if self.first then if self.first then
local prevent = {"S", "Z", "O"} local prevent = {"S", "Z", "O"}
repeat repeat
index = math.random(#self.pool) index = math.random(#self.pool)
x = self.pool[index] x = self.pool[index]
until not inHistory(x, prevent) until not inHistory(x, prevent)
self.first = false self.first = false
else else
for i = 1, 6 do for i = 1, 6 do
index = math.random(#self.pool) index = math.random(#self.pool)
x = self.pool[index] x = self.pool[index]
if not inHistory(x, self.history) or i == 6 then if not inHistory(x, self.history) or i == 6 then
break break
end end
end end
end end
self.pool[index] = self:updateHistory(x) self.pool[index] = self:updateHistory(x)
return x return x
end end
function History6Rolls35PoolRandomizer:updateHistory(shape) function History6Rolls35PoolRandomizer:updateHistory(shape)
table.remove(self.history, 1) table.remove(self.history, 1)
table.insert(self.history, shape) table.insert(self.history, shape)
local highdrought local highdrought
local highdroughtcount = 0 local highdroughtcount = 0
for k, v in pairs(self.droughts) do for k, v in pairs(self.droughts) do
if k == shape then if k == shape then
self.droughts[k] = 0 self.droughts[k] = 0
else else
self.droughts[k] = v + 1 self.droughts[k] = v + 1
if v >= highdroughtcount then if v >= highdroughtcount then
highdrought = k highdrought = k
highdroughtcount = v highdroughtcount = v
end end
end end
end end
return highdrought return highdrought
end end

View File

@ -1,30 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local RecursiveRandomizer = Randomizer:extend()
function RecursiveRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
end
function RecursiveRandomizer:generatePiece()
--if next(self.bag) == nil then
-- self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
--end
local x = math.random(table.getn(self.bag) + 1)
while x == table.getn(self.bag) + 1 do
--print("Refill piece pulled")
table.insert(self.bag, "I")
table.insert(self.bag, "J")
table.insert(self.bag, "L")
table.insert(self.bag, "O")
table.insert(self.bag, "S")
table.insert(self.bag, "T")
table.insert(self.bag, "Z")
x = math.random(table.getn(self.bag) + 1)
end
--print("Number of pieces in bag: "..table.getn(self.bag))
--print("Bag: "..table.concat(self.bag, ", "))
return table.remove(self.bag, x)
end
return RecursiveRandomizer

View File

@ -1,19 +0,0 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local SegaRandomizer = Randomizer:extend()
function SegaRandomizer:initialize()
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
self.sequence = {}
for i = 1, 1000 do
self.sequence[i] = self.bag[math.random(table.getn(self.bag))]
end
self.counter = 0
end
function SegaRandomizer:generatePiece()
self.counter = self.counter + 1
return self.sequence[self.counter % 1000 + 1]
end
return SegaRandomizer

View File

@ -82,18 +82,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
) and ( ) and (
piece.rotation == 0 or piece.rotation == 2 piece.rotation == 0 or piece.rotation == 2
) then ) then
local offsets = new_piece:getBlockOffsets() local offsets = new_piece:getBlockOffsets()
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end) table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
if offset.x == 0 then if offset.x == 0 then
return return
else else
break break
end end
end end
end end
end end
-- kick right, kick left -- kick right, kick left
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
@ -110,7 +110,14 @@ function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function ARS:get180RotationValue() return 3 end function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then
return 3
else
return 1
end
end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -7,13 +7,13 @@ ARS.name = "ACE-ARS"
ARS.hash = "ArikaACE" ARS.hash = "ArikaACE"
ARS.colourscheme = { ARS.colourscheme = {
I = "C", I = "C",
L = "O", L = "O",
J = "B", J = "B",
S = "G", S = "G",
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
} }
ARS.softdrop_lock = false ARS.softdrop_lock = false
@ -98,18 +98,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
) and ( ) and (
piece.rotation == 0 or piece.rotation == 2 piece.rotation == 0 or piece.rotation == 2
) then ) then
local offsets = new_piece:getBlockOffsets() local offsets = new_piece:getBlockOffsets()
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end) table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
if offset.x == 0 then if offset.x == 0 then
return return
else else
break break
end end
end end
end end
end end
if piece.shape == "I" then if piece.shape == "I" then
-- special kick rules for I -- special kick rules for I
@ -138,20 +138,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
end end
end end
else else
-- kick right, kick left -- kick right, kick left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
elseif piece.shape == "T" elseif piece.shape == "T"
and new_piece.rotation == 0 and new_piece.rotation == 0
and piece.floorkick == 0 and piece.floorkick == 0
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
then then
-- T floorkick -- T floorkick
piece.floorkick = piece.floorkick + 1 piece.floorkick = piece.floorkick + 1
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
end end
end end
end end
@ -185,7 +185,14 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return 3 end function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then
return 3
else
return 1
end
end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -85,18 +85,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
) and ( ) and (
piece.rotation == 0 or piece.rotation == 2 piece.rotation == 0 or piece.rotation == 2
) then ) then
local offsets = new_piece:getBlockOffsets() local offsets = new_piece:getBlockOffsets()
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end) table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
if offset.x == 0 then if offset.x == 0 then
return return
else else
break break
end end
end end
end end
end end
if piece.shape == "I" then if piece.shape == "I" then
-- special kick rules for I -- special kick rules for I
@ -125,20 +125,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
end end
end end
else else
-- kick right, kick left -- kick right, kick left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
elseif piece.shape == "T" elseif piece.shape == "T"
and new_piece.rotation == 0 and new_piece.rotation == 0
and piece.floorkick == 0 and piece.floorkick == 0
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
then then
-- T floorkick -- T floorkick
piece.floorkick = piece.floorkick + 1 piece.floorkick = piece.floorkick + 1
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
end end
end end
end end
@ -172,7 +172,14 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return 3 end function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then
return 3
else
return 1
end
end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -7,19 +7,17 @@ SRS.name = "ACE-SRS"
SRS.hash = "ACE Standard" SRS.hash = "ACE Standard"
SRS.world = true SRS.world = true
SRS.colourscheme = { SRS.colourscheme = {
I = "C", I = "C",
L = "O", L = "O",
J = "B", J = "B",
S = "G", S = "G",
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
} }
SRS.softdrop_lock = false SRS.softdrop_lock = false
SRS.harddrop_lock = true SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true
SRS.spawn_positions = { SRS.spawn_positions = {
I = { x=5, y=2 }, I = { x=5, y=2 },
J = { x=4, y=3 }, J = { x=4, y=3 },
@ -186,5 +184,12 @@ function SRS:onPieceRotate(piece, grid)
end end
end end
function SRS:get180RotationValue() return 3 end function SRS:get180RotationValue()
if config.gamesettings.world_reverse == 1 then
return 1
else
return 3
end
end
return SRS return SRS

View File

@ -85,18 +85,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
) and ( ) and (
piece.rotation == 0 or piece.rotation == 2 piece.rotation == 0 or piece.rotation == 2
) then ) then
local offsets = new_piece:getBlockOffsets() local offsets = new_piece:getBlockOffsets()
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end) table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
if offset.x == 0 then if offset.x == 0 then
return return
else else
break break
end end
end end
end end
end end
if piece.shape == "I" then if piece.shape == "I" then
-- special kick rules for I -- special kick rules for I
@ -125,20 +125,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
end end
end end
else else
-- kick right, kick left -- kick right, kick left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0}) piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
elseif piece.shape == "T" elseif piece.shape == "T"
and new_piece.rotation == 0 and new_piece.rotation == 0
and piece.floorkick == 0 and piece.floorkick == 0
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
then then
-- T floorkick -- T floorkick
piece.floorkick = piece.floorkick + 1 piece.floorkick = piece.floorkick + 1
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
end end
end end
end end
@ -151,7 +151,14 @@ function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function ARS:get180RotationValue() return 3 end function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then
return 3
else
return 1
end
end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -364,9 +364,9 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial)
if rot_dir == 0 then return end if rot_dir == 0 then return end
if self.world and config.gamesettings.world_reverse == 2 then if self.world and config.gamesettings.world_reverse == 2 then
rot_dir = 4 - rot_dir rot_dir = 4 - rot_dir
end end
local new_piece = piece:withRelativeRotation(rot_dir) local new_piece = piece:withRelativeRotation(rot_dir)
self:attemptWallkicks(piece, new_piece, rot_dir, grid) self:attemptWallkicks(piece, new_piece, rot_dir, grid)

View File

@ -9,13 +9,13 @@ Ruleset.hash = ""
-- Arika-type ruleset defaults -- Arika-type ruleset defaults
Ruleset.world = false Ruleset.world = false
Ruleset.colourscheme = { Ruleset.colourscheme = {
I = "R", I = "R",
L = "O", L = "O",
J = "B", J = "B",
S = "M", S = "M",
Z = "G", Z = "G",
O = "Y", O = "Y",
T = "C", T = "C",
} }
Ruleset.softdrop_lock = true Ruleset.softdrop_lock = true
Ruleset.harddrop_lock = false Ruleset.harddrop_lock = false
@ -24,6 +24,60 @@ Ruleset.enable_IRS_wallkicks = false
-- Component functions. -- Component functions.
function Ruleset:new()
if config.gamesettings.piece_colour == 1 then
blocks["bone"] = (not self.world) and
{
R = love.graphics.newImage("res/img/bone.png"),
O = love.graphics.newImage("res/img/bone.png"),
Y = love.graphics.newImage("res/img/bone.png"),
G = love.graphics.newImage("res/img/bone.png"),
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
} or {
R = love.graphics.newImage("res/img/bonew.png"),
O = love.graphics.newImage("res/img/bonew.png"),
Y = love.graphics.newImage("res/img/bonew.png"),
G = love.graphics.newImage("res/img/bonew.png"),
C = love.graphics.newImage("res/img/bonew.png"),
B = love.graphics.newImage("res/img/bonew.png"),
M = love.graphics.newImage("res/img/bonew.png"),
F = love.graphics.newImage("res/img/bonew.png"),
A = love.graphics.newImage("res/img/bonew.png"),
X = love.graphics.newImage("res/img/bonew.png"),
}
else
blocks["bone"] = (config.gamesettings.piece_colour == 2) and
{
R = love.graphics.newImage("res/img/bone.png"),
O = love.graphics.newImage("res/img/bone.png"),
Y = love.graphics.newImage("res/img/bone.png"),
G = love.graphics.newImage("res/img/bone.png"),
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
} or {
R = love.graphics.newImage("res/img/bonew.png"),
O = love.graphics.newImage("res/img/bonew.png"),
Y = love.graphics.newImage("res/img/bonew.png"),
G = love.graphics.newImage("res/img/bonew.png"),
C = love.graphics.newImage("res/img/bonew.png"),
B = love.graphics.newImage("res/img/bonew.png"),
M = love.graphics.newImage("res/img/bonew.png"),
F = love.graphics.newImage("res/img/bonew.png"),
A = love.graphics.newImage("res/img/bonew.png"),
X = love.graphics.newImage("res/img/bonew.png"),
}
end
end
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial) function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
local new_inputs = {} local new_inputs = {}
@ -53,7 +107,7 @@ function Ruleset:attemptRotate(new_inputs, piece, grid, initial)
end end
if rot_dir == 0 then return end if rot_dir == 0 then return end
if self.world and config.gamesettings.world_reverse == 2 then if config.gamesettings.world_reverse == 3 or (self.world and config.gamesettings.world_reverse == 2) then
rot_dir = 4 - rot_dir rot_dir = 4 - rot_dir
end end
@ -134,7 +188,7 @@ function Ruleset:initializePiece(
else else
spawn_positions = self.spawn_positions spawn_positions = self.spawn_positions
end end
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour] local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
local piece = Piece(data.shape, data.orientation - 1, { local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_positions[data.shape].x, x = spawn_positions[data.shape].x,

View File

@ -7,13 +7,13 @@ SRS.name = "Guideline SRS"
SRS.hash = "Standard" SRS.hash = "Standard"
SRS.world = true SRS.world = true
SRS.colourscheme = { SRS.colourscheme = {
I = "C", I = "C",
L = "O", L = "O",
J = "B", J = "B",
S = "G", S = "G",
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
} }
SRS.softdrop_lock = false SRS.softdrop_lock = false
SRS.harddrop_lock = true SRS.harddrop_lock = true
@ -132,13 +132,13 @@ SRS.wallkicks_line = {
}; };
function SRS:check_new_low(piece) function SRS:check_new_low(piece)
for _, block in pairs(piece:getBlockOffsets()) do for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y local y = piece.position.y + block.y
if y > piece.lowest_y then if y > piece.lowest_y then
piece.manipulations = 0 piece.manipulations = 0
piece.lowest_y = y piece.lowest_y = y
end end
end end
end end
-- Component functions. -- Component functions.
@ -170,16 +170,16 @@ end
function SRS:onPieceCreate(piece, grid) function SRS:onPieceCreate(piece, grid)
piece.manipulations = 0 piece.manipulations = 0
piece.lowest_y = -math.huge piece.lowest_y = -math.huge
end end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
self:check_new_low(piece) self:check_new_low(piece)
if piece.manipulations >= 15 and piece:isDropBlocked(grid) then if piece.manipulations >= 15 and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
else else
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
end end
function SRS:onPieceMove(piece, grid) function SRS:onPieceMove(piece, grid)
@ -194,8 +194,8 @@ end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:check_new_low(piece) self:check_new_low(piece)
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
if piece.manipulations >= 15 then if piece.manipulations >= 15 then
piece.locked = true piece.locked = true

View File

@ -7,19 +7,17 @@ SRS.name = "Ti-World"
SRS.hash = "Bad I-kicks" SRS.hash = "Bad I-kicks"
SRS.world = true SRS.world = true
SRS.colourscheme = { SRS.colourscheme = {
I = "C", I = "C",
L = "O", L = "O",
J = "B", J = "B",
S = "G", S = "G",
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
} }
SRS.softdrop_lock = false SRS.softdrop_lock = false
SRS.harddrop_lock = true SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true
SRS.spawn_positions = { SRS.spawn_positions = {
I = { x=5, y=4 }, I = { x=5, y=4 },
J = { x=4, y=5 }, J = { x=4, y=5 },
@ -187,5 +185,12 @@ function SRS:onPieceRotate(piece, grid)
end end
end end
function SRS:get180RotationValue() return 3 end function SRS:get180RotationValue()
if config.gamesettings.world_reverse == 1 then
return 1
else
return 3
end
end
return SRS return SRS

View File

@ -1,102 +0,0 @@
Piece = require("tetris.components.piece")
local BONKERS = {}
BONKERS.name = "B.O.N.K.E.R.S."
BONKERS.hash = "Bonkers"
BONKERS.spawn_positions = {
I = { x=5, y=4 },
J = { x=4, y=5 },
L = { x=4, y=5 },
O = { x=5, y=5 },
S = { x=4, y=5 },
T = { x=4, y=5 },
Z = { x=4, y=5 },
}
BONKERS.block_offsets = {
I={
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
},
J={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
},
L={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
},
O={
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
},
S={
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
},
T={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
},
Z={
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
}
}
-- Component functions.
function BONKERS:attemptWallkicks(piece, new_piece, rot_dir, grid)
if piece.shape == "O" then
break
elseif piece.shape == "I" then
horizontal_kicks = {0, 1, -1, 2, -2}
else
horizontal_kicks = {0, 1, -1}
end
for y_offset = 20, new_piece.position.y - 24, -1 do
for idx, x_offset in pairs(horizontal_kicks) do
local offset = {x=x_offset, y=y_offset}
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
piece.lock_delay = 0 -- rotate reset
return
end
end
end
end
function BONKERS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset
end
function BONKERS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
end
function BONKERS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset
end
return BONKERS

View File

@ -1,235 +0,0 @@
Piece = require("tetris.components.piece")
require("funcs")
local SRS = {}
SRS.name = "SHIRASE"
SRS.hash = "Shirase"
SRS.spawn_positions = {
I = { x=5, y=4 },
J = { x=4, y=5 },
L = { x=4, y=5 },
O = { x=5, y=5 },
S = { x=4, y=5 },
T = { x=4, y=5 },
Z = { x=4, y=5 },
}
SRS.block_offsets = {
I={
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
},
J={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
},
L={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
},
O={
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
},
S={
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
},
T={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
},
Z={
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
}
}
SRS.wallkicks_3x3 = {
[0]={
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[2]={{x=0, y=1}, {x=0, y=-1}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[1]={
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
[3]={{x=0, y=1}, {x=0, y=-1}},
},
[2]={
[0]={{x=0, y=1}, {x=0, y=-1}},
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
},
[3]={
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
[1]={{x=0, y=1}, {x=0, y=-1}},
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
},
};
SRS.wallkicks_line = {
[0]={
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
[2]={},
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
},
[1]={
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
},
[2]={
[0]={},
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
},
[3]={
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
},
};
local basicOffsets = {
[0] = { x = 1, y = 0 },
[1] = { x = 0, y = 1 },
[2] = { x = -1, y = 0 },
[3] = { x = 0, y = -1 }
}
-- Component functions.
local function rotatePiece(inputs, piece, grid, prev_inputs)
local new_inputs = {}
for input, value in pairs(inputs) do
if value and not prev_inputs[input] then
new_inputs[input] = true
end
end
local rot_dir = 0
if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
rot_dir = 3
elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
rot_dir = 1
elseif (new_inputs["rotate_180"]) then
rot_dir = 2
end
while rot_dir ~= 0 do
rotated_piece = piece:withRelativeRotation(rot_dir)
rotation_offset = vAdd(
basicOffsets[piece.rotation],
vNeg(basicOffsets[rotated_piece.rotation])
)
new_piece = rotated_piece:withOffset(rotation_offset)
if (grid:canPlacePiece(new_piece)) then
piece:setRelativeRotation(rot_dir)
piece:setOffset(rotation_offset)
piece.lock_delay = 0 -- rotate reset
break
end
if piece.shape == "I" then
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
else
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
end
for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
piece:setRelativeRotation(rot_dir)
piece:setOffset(vAdd(offset, rotation_offset))
piece.lock_delay = 0 -- rotate reset
rot_dir = 0
end
if rot_dir == 0 then
break
end
end
rot_dir = 0
end
-- prev_inputs becomes the previous inputs
for input, value in pairs(inputs) do
prev_inputs[input] = inputs[input]
end
end
local function movePiece(piece, grid, move)
if move == "left" then
if not piece:isMoveBlocked(grid, {x=-1, y=0}) then
piece.lock_delay = 0 -- move reset
end
piece:moveInGrid({x=-1, y=0}, 1, grid)
elseif move == "right" then
if not piece:isMoveBlocked(grid, {x=1, y=0}) then
piece.lock_delay = 0 -- move reset
end
piece:moveInGrid({x=1, y=0}, 1, grid)
end
end
local function dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
local y = piece.position.y
if inputs["down"] == true and drop_locked == false then
piece:addGravity(gravity + 1, grid):lockIfBottomed(grid)
elseif inputs["up"] == true then
if piece:isDropBlocked(grid) then
return
end
piece:dropToBottom(grid)
else
piece:addGravity(gravity, grid)
end
if piece.position.y ~= y then -- step reset
piece.lock_delay = 0
end
end
local function lockPiece(piece, grid, lock_delay)
if piece:isDropBlocked(grid) and piece.lock_delay >= lock_delay then
piece.locked = true
end
end
function SRS.initializePiece(inputs, data, grid, gravity, prev_inputs, move, lock_delay, drop_speed, drop_locked)
local piece = Piece(shape, 0, {
x = SRS.spawn_positions[shape].x,
y = SRS.spawn_positions[shape].y
}, SRS.block_offsets, 0, 0)
-- have to copy that object otherwise it gets referenced
rotatePiece(inputs, piece, grid, {})
dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
return piece
end
function SRS.processPiece(inputs, piece, grid, gravity, prev_inputs, move, lock_delay, drop_speed, drop_locked)
rotatePiece(inputs, piece, grid, prev_inputs)
movePiece(piece, grid, move)
dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
lockPiece(piece, grid, lock_delay)
end
return SRS

View File

@ -1,174 +0,0 @@
Piece = require("tetris.components.piece")
local BONKERS = {}
BONKERS.name = "SUPER302"
BONKERS.hash = "Super302"
BONKERS.spawn_positions = {
I = { x=5, y=4 },
J = { x=4, y=5 },
L = { x=4, y=5 },
O = { x=5, y=5 },
S = { x=4, y=5 },
T = { x=4, y=5 },
Z = { x=4, y=5 },
}
BONKERS.block_offsets = {
I={
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
},
J={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
},
L={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
},
O={
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
},
S={
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
},
T={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
},
Z={
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
}
}
-- Component functions.
local function rotatePiece(inputs, piece, grid, prev_inputs)
local new_inputs = {}
for input, value in pairs(inputs) do
if value and not prev_inputs[input] then
new_inputs[input] = true
end
end
local rot_dir = 0
if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
rot_dir = 3
elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
rot_dir = 1
elseif (new_inputs["rotate_180"]) then
rot_dir = 2
end
while rot_dir ~= 0 do
if piece.filled then break end
new_piece = piece:withRelativeRotation(rot_dir)
if (grid:canPlacePiece(new_piece)) and piece.shape ~= "O" then
piece:setRelativeRotation(rot_dir)
piece.lock_delay = 0 -- rotate reset
else
-- set the piece to occupy the whole grid
piece.filled = true
unfilled_block_offsets = {}
for y = 4, 23 do
for x = 0, 9 do
if not grid:isOccupied(x, y) then
table.insert(unfilled_block_offsets, {x=x, y=y})
end
end
end
piece.position = {x=0, y=0}
piece.getBlockOffsets = function(piece)
return unfilled_block_offsets
end
piece.isDropBlocked = function(piece)
return true
end
end
rot_dir = 0
end
-- prev_inputs becomes the previous inputs
for input, value in pairs(inputs) do
prev_inputs[input] = inputs[input]
end
end
local function movePiece(piece, grid, move)
if move == "left" then
if not piece:isMoveBlocked(grid, {x=-1, y=0}) then
piece.lock_delay = 0 -- move reset
end
piece:moveInGrid({x=-1, y=0}, 1, grid)
elseif move == "right" then
if not piece:isMoveBlocked(grid, {x=1, y=0}) then
piece.lock_delay = 0 -- move reset
end
piece:moveInGrid({x=1, y=0}, 1, grid)
end
end
local function dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
local y = piece.position.y
if inputs["down"] == true and drop_locked == false then
piece:addGravity(gravity + 1, grid):lockIfBottomed(grid)
elseif inputs["up"] == true then
if piece:isDropBlocked(grid) then
return
end
piece:dropToBottom(grid)
else
piece:addGravity(gravity, grid)
end
if piece.position.y ~= y then -- step reset
piece.lock_delay = 0
end
end
local function lockPiece(piece, grid, lock_delay)
if piece:isDropBlocked(grid) and piece.lock_delay >= lock_delay then
piece.locked = true
end
end
function BONKERS.initializePiece(inputs, data, grid, gravity, prev_inputs, move, lock_delay, drop_speed, drop_locked)
local piece = Piece(shape, 0, {
x = BONKERS.spawn_positions[shape].x,
y = BONKERS.spawn_positions[shape].y
}, BONKERS.block_offsets, 0, 0)
-- have to copy that object otherwise it gets referenced
rotatePiece(inputs, piece, grid, {})
dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
return piece
end
function BONKERS.processPiece(inputs, piece, grid, gravity, prev_inputs, move, lock_delay, drop_speed, drop_locked)
rotatePiece(inputs, piece, grid, prev_inputs)
movePiece(piece, grid, move)
dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked)
lockPiece(piece, grid, lock_delay)
end
return BONKERS

View File

@ -1,133 +0,0 @@
local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.ruleset'
local Tengen = Ruleset:extend()
Tengen.name = "Tengen"
Tengen.hash = "Tengen"
Tengen.spawn_positions = {
I = { x=3, y=4 },
J = { x=4, y=4 },
L = { x=4, y=4 },
O = { x=5, y=4 },
S = { x=4, y=4 },
T = { x=4, y=4 },
Z = { x=4, y=4 },
}
Tengen.block_offsets = {
I={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
},
J={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=2, y=1} },
{ {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=0, y=2} },
{ {x=0, y=0}, {x=0, y=1}, {x=1, y=1}, {x=2, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=1, y=0} },
},
L={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=0, y=1} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=1, y=2} },
{ {x=2, y=0}, {x=0, y=1}, {x=1, y=1}, {x=2, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=1, y=2} },
},
O={
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=0, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=0, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=0, y=1} },
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=0, y=1} },
},
-- up to here
S={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
},
T={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
},
Z={
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
{ {x=0, y=0}, {x=1, y=0}, {x=2, y=0}, {x=3, y=0} },
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=2}, {x=0, y=3} },
}
}
-- Component functions.
function Tengen:attemptWallkicks(piece, new_piece, rot_dir, grid)
-- O doesn't kick
if (piece.shape == "O") then return end
-- center column rule
if (
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
) and (
piece.rotation == 0 or piece.rotation == 2
) and (
grid:isOccupied(piece.position.x, piece.position.y) or
grid:isOccupied(piece.position.x, piece.position.y - 1) or
grid:isOccupied(piece.position.x, piece.position.y - 2)
) then return end
if piece.shape == "I" then
-- special kick rules for I
if new_piece.rotation == 0 or new_piece.rotation == 2 then
-- kick right, right2, left
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
self:onPieceRotate(piece, grid)
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
self:onPieceRotate(piece, grid)
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
self:onPieceRotate(piece, grid)
end
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then
-- kick up, up2
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
self:onPieceRotate(piece, grid)
piece.floorkick = 1
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
self:onPieceRotate(piece, grid)
piece.floorkick = 1
end
end
elseif piece.shape ~= "I" then
-- kick right, kick left
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
elseif (grid:canPlacePiece(new_piece:withOffset({x=-1, y=0}))) then
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
end
else
end
end
function Tengen:onPieceCreate(piece, grid)
piece.floorkick = 0
end
function Tengen:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset
end
function Tengen:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
function Tengen:getDefaultOrientation() return 3 end -- downward facing pieces by default
return Tengen