diff --git a/.gitignore b/.gitignore index 0bdafc3..c716095 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.love dist/*.zip dist/**/cambridge.exe +dist/**/libs +dist/**/*.md \ No newline at end of file diff --git a/README.md b/README.md index cd15fa7..7b755b6 100644 --- a/README.md +++ b/README.md @@ -5,48 +5,55 @@ Cambridge Welcome to Cambridge, the next open-source falling-block game engine! -This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac) 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 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! -- [joezeng](https://github.com/joezeng) for the original project. -- [Hailey](https://github.com/haileylgbt) for some miscellaneous assets. -- CylinderKnot for an amazing gamemode. -- MarkGamed7794 for some miscellaneous contributions. -- Mizu for the Cambridge logo and the [Cambridge launcher](https://github.com/rexxt/cambridge-launcher). -- MattMayuga for the Cambridge banner. +- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation! + +The following people in no particular order also helped with the project: +- [Hailey](https://github.com/haileylgbt) +- CylinderKnot +- MarkGamed7794 +- [Mizu](https://github.com/rexxt) +- MattMayuga +- Kitaru +- switchpalacecorner +- [sinefuse](https://github.com/sinefuse) +- [2Tie](https://github.com/2Tie) +- [nightmareci](https://github.com/nightmareci) +- [MyPasswordIsWeak](https://github.com/MyPasswordIsWeak) ![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png) -Installation instructions -------------------------- - -Pre-built releases are available on the releases page. +Playing the game +---------------- ### 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 @@ -54,14 +61,21 @@ If you haven't already, install `love` with your favourite package manager (Home 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: - love . + love . 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 ------- diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..a5c4cc2 --- /dev/null +++ b/clean.bat @@ -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 \ No newline at end of file diff --git a/docs/gamemodes.md b/docs/gamemodes.md index 19b755e..d3fd10e 100644 --- a/docs/gamemodes.md +++ b/docs/gamemodes.md @@ -4,17 +4,17 @@ Game modes 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. - * C84 - The original version from the Electronika 60. - * C88 - Sega Tetris. - * C89 - Nintendo / NES Tetris. + * C84 - The original version from the Electronika 60. + * C88 - Sega Tetris. + * C89 - Nintendo / NES Tetris. * 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). - * A2 - Tetris The Absolute The Grand Master 2 PLUS. - * A3 - Tetris The Grand Master 3 Terror-Instinct. - * AX - Tetris The Grand Master ACE (X for Xbox). + * A1 - Tetris The Grand Master (the original from 1998). + * A2 - Tetris The Absolute The Grand Master 2 PLUS. + * A3 - Tetris The Grand Master 3 Terror-Instinct. + * AX - Tetris The Grand Master ACE (X for Xbox). * The "G" series stand for "Guideline" games, or games that follow the Tetris Guideline. - * GF - Tetris Friends (2007-2019) - * GJ - Tetris Online Japan (2005-2011) + * GF - Tetris Friends (2007-2019) + * GJ - Tetris Online Japan (2005-2011) * N stands for Nullpomino, only used for Phantom Mania N. MARATHON diff --git a/funcs.lua b/funcs.lua index ea195f6..cd1bed1 100644 --- a/funcs.lua +++ b/funcs.lua @@ -1,11 +1,11 @@ function copy(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 - local meta = getmetatable(t) - local target = {} - for k, v in pairs(t) do target[k] = v end - setmetatable(target, meta) - return target + local meta = getmetatable(t) + local target = {} + for k, v in pairs(t) do target[k] = v end + setmetatable(target, meta) + return target end function strTrueValues(tbl) @@ -64,6 +64,6 @@ function formatBigNum(number) end function Mod1(n, m) - -- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1]) - return ((n-1) % m) + 1 + -- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1]) + return ((n-1) % m) + 1 end \ No newline at end of file diff --git a/libs/discordRPC.lua b/libs/discordRPC.lua index 1b9f5e4..6a21b8c 100644 --- a/libs/discordRPC.lua +++ b/libs/discordRPC.lua @@ -5,12 +5,26 @@ local ffi = require "ffi" local osname = love.system.getOS() 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 - discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.so") + discordRPClib = ffi.load(source.."/libs/discord-rpc.so") 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 - discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.dll") + discordRPClib = ffi.load(source.."/libs/discord-rpc.dll") else -- Else it crashes later on error(string.format("Discord rpc not supported on platform (%s)", osname)) diff --git a/load/graphics.lua b/load/graphics.lua index b79fc74..a0525b0 100644 --- a/load/graphics.lua +++ b/load/graphics.lua @@ -20,8 +20,8 @@ backgrounds = { love.graphics.newImage("res/backgrounds/1800-railways.png"), love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"), title = love.graphics.newImage("res/backgrounds/title_v0.1.png"), - input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"), - game_config = love.graphics.newImage("res/backgrounds/options-gears.png"), + input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"), + game_config = love.graphics.newImage("res/backgrounds/options-gears.png"), } blocks = { @@ -33,6 +33,8 @@ blocks = { C = love.graphics.newImage("res/img/s2.png"), B = love.graphics.newImage("res/img/s4.png"), M = love.graphics.newImage("res/img/s5.png"), + F = love.graphics.newImage("res/img/s9.png"), + A = love.graphics.newImage("res/img/s8.png"), X = love.graphics.newImage("res/img/s9.png"), }, ["bone"] = { @@ -43,29 +45,31 @@ blocks = { C = love.graphics.newImage("res/img/bone.png"), B = love.graphics.newImage("res/img/bone.png"), M = love.graphics.newImage("res/img/bone.png"), + F = love.graphics.newImage("res/img/bone.png"), + A = love.graphics.newImage("res/img/bone.png"), X = love.graphics.newImage("res/img/bone.png"), } } ColourSchemes = { - Arika = { - I = "R", - L = "O", - J = "B", - S = "M", - Z = "G", - O = "Y", - T = "C", - }, - TTC = { - I = "C", - L = "O", - J = "B", - S = "G", - Z = "R", - O = "Y", - T = "M", - }, + Arika = { + I = "R", + L = "O", + J = "B", + S = "M", + Z = "G", + O = "Y", + T = "C", + }, + TTC = { + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", + }, } for name, blockset in pairs(blocks) do @@ -80,4 +84,4 @@ misc_graphics = { go = love.graphics.newImage("res/img/go.png"), select_mode = love.graphics.newImage("res/img/select_mode.png"), strike = love.graphics.newImage("res/img/strike.png"), -} +} \ No newline at end of file diff --git a/load/rpc.lua b/load/rpc.lua index 5dc3de8..1dc45c0 100644 --- a/load/rpc.lua +++ b/load/rpc.lua @@ -1,58 +1,58 @@ print("Loading discord RPC...") DiscordRPC = { - loaded = false + loaded = false } local success, RPC = pcall(require, "libs.discordRPC") if success then - DiscordRPC.loaded = true - DiscordRPC.appId = "599778517789573120" + DiscordRPC.loaded = true + DiscordRPC.appId = "599778517789573120" - function RPC.ready(userId, username, discriminator, avatar) - print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar)) - end + function RPC.ready(userId, username, discriminator, avatar) + print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar)) + end - function RPC.disconnected(errorCode, message) - print(string.format("Discord: disconnected (%d: %s)", errorCode, message)) - end + function RPC.disconnected(errorCode, message) + print(string.format("Discord: disconnected (%d: %s)", errorCode, message)) + end - function RPC.errored(errorCode, message) - print(string.format("Discord: error (%d: %s)", errorCode, message)) - end + function RPC.errored(errorCode, message) + print(string.format("Discord: error (%d: %s)", errorCode, message)) + end - function RPC.joinGame(joinSecret) - print(string.format("Discord: join (%s)", joinSecret)) - end + function RPC.joinGame(joinSecret) + print(string.format("Discord: join (%s)", joinSecret)) + end - function RPC.spectateGame(spectateSecret) - print(string.format("Discord: spectate (%s)", spectateSecret)) - end + function RPC.spectateGame(spectateSecret) + print(string.format("Discord: spectate (%s)", spectateSecret)) + end - function RPC.joinRequest(userId, username, discriminator, avatar) - print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar)) - RPC.respond(userId, "yes") - end + function RPC.joinRequest(userId, username, discriminator, avatar) + print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar)) + RPC.respond(userId, "yes") + end - RPC.initialize(DiscordRPC.appId, true) - local now = os.time(os.date("*t")) - - DiscordRPC.RPC = RPC - print("DiscordRPC successfully loaded.") + RPC.initialize(DiscordRPC.appId, true) + local now = os.time(os.date("*t")) + + DiscordRPC.RPC = RPC + print("DiscordRPC successfully loaded.") else - print("DiscordRPC failed to load!") - print(RPC) + print("DiscordRPC failed to load!") + print(RPC) end DiscordRPC.presence = { - startTimestamp = now, - details = "Loading game...", - state = "", - largeImageKey = "icon2", - largeImageText = "Arcade Stacker", - smallImageKey = "", - smallImageText = "" + startTimestamp = now, + details = "Loading game...", + state = "", + largeImageKey = "icon2", + largeImageText = "Arcade Stacker", + smallImageKey = "", + smallImageText = "" } function DiscordRPC:update(newstuff) - for k, v in pairs(newstuff) do self.presence[k] = v end - if self.loaded then self.RPC.updatePresence(self.presence) end + for k, v in pairs(newstuff) do self.presence[k] = v end + if self.loaded then self.RPC.updatePresence(self.presence) end end diff --git a/main.lua b/main.lua index 33d1f4d..9243edc 100644 --- a/main.lua +++ b/main.lua @@ -15,21 +15,42 @@ function love.load() love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); - if not config.gamesettings then config.gamesettings = {} end - for _, option in ipairs(GameConfigScene.options) do - if not config.gamesettings[option[1]] then - config.gamesettings[option[1]] = 1 - end - end - + if not config.gamesettings then config.gamesettings = {} end + for _, option in ipairs(GameConfigScene.options) do + if not config.gamesettings[option[1]] then + config.gamesettings[option[1]] = 1 + end + end + if not config.input then - config.input = {} scene = InputConfigScene() else if config.current_mode then current_mode = config.current_mode end if config.current_ruleset then current_ruleset = config.current_ruleset end scene = TitleScene() 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 local TARGET_FPS = 60 @@ -88,13 +109,124 @@ function love.draw() love.graphics.pop() end -function love.keypressed(key, scancode, isrepeat) +function love.keypressed(key, scancode) -- global hotkeys if scancode == "f4" then config["fullscreen"] = not 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 - 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 diff --git a/package.bat b/package.bat new file mode 100644 index 0000000..e79a40e --- /dev/null +++ b/package.bat @@ -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 \ No newline at end of file diff --git a/release.bat b/release.bat new file mode 100644 index 0000000..8366080 --- /dev/null +++ b/release.bat @@ -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 ..\.. \ No newline at end of file diff --git a/res/img/bonew.png b/res/img/bonew.png new file mode 100644 index 0000000..5fc2bbb Binary files /dev/null and b/res/img/bonew.png differ diff --git a/res/img/s8.png b/res/img/s8.png new file mode 100644 index 0000000..3e063f8 Binary files /dev/null and b/res/img/s8.png differ diff --git a/scene.lua b/scene.lua index f2b7a51..809a2df 100644 --- a/scene.lua +++ b/scene.lua @@ -5,7 +5,8 @@ Scene = Object:extend() function Scene:new() end function Scene:update() end function Scene:render() end -function Scene:onKeyPress() end +function Scene:onInputPress() end +function Scene:onInputRelease() end ExitScene = require "scene.exit" GameScene = require "scene.game" diff --git a/scene/config.lua b/scene/config.lua index 630cf63..3de9d0b 100644 --- a/scene/config.lua +++ b/scene/config.lua @@ -17,7 +17,7 @@ function ConfigScene:changeOption(rel) self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1 end -function ConfigScene:onKeyPress(e) +function ConfigScene:onInputPress(e) end return ConfigScene diff --git a/scene/exit.lua b/scene/exit.lua index 3ae1c55..bd1fd16 100644 --- a/scene/exit.lua +++ b/scene/exit.lua @@ -7,7 +7,7 @@ function ExitScene:new() end function ExitScene:update() - love.event.quit() + love.event.quit() end function ExitScene:render() @@ -16,7 +16,7 @@ end function ExitScene:changeOption(rel) end -function ExitScene:onKeyPress(e) +function ExitScene:onInputPress(e) end return ExitScene diff --git a/scene/game.lua b/scene/game.lua index c0a1d1f..2629ae6 100644 --- a/scene/game.lua +++ b/scene/game.lua @@ -1,30 +1,40 @@ local GameScene = Scene:extend() + +GameScene.title = "Game" + require 'load.save' function GameScene:new(game_mode, ruleset) + self.retry_mode = game_mode + self.retry_ruleset = ruleset self.game = game_mode() self.ruleset = 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({ - details = self.game.rpc_details, - state = self.game.name, - }) + details = self.game.rpc_details, + state = self.game.name, + }) end function GameScene:update() if love.window.hasFocus() then - self.game:update({ - left = love.keyboard.isScancodeDown(config.input.left), - right = love.keyboard.isScancodeDown(config.input.right), - up = love.keyboard.isScancodeDown(config.input.up), - down = love.keyboard.isScancodeDown(config.input.down), - 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) + local inputs = {} + for input, value in pairs(self.inputs) do + inputs[input] = value + end + self.game:update(inputs, self.ruleset) end self.game.grid:update() @@ -60,23 +70,24 @@ function GameScene:render() end -function GameScene:onKeyPress(e) - if (self.game.completed) and - (e.scancode == "return" or e.scancode == "escape") and e.isRepeat == false then +function GameScene:onInputPress(e) + if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then highscore_entry = self.game:getHighscoreData() highscore_hash = self.game.hash .. "-" .. self.ruleset.hash submitHighscore(highscore_hash, highscore_entry) + scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) or ModeSelectScene() + elseif e.input == "retry" then + scene = GameScene(self.retry_mode, self.retry_ruleset) + elseif e.input == "menu_back" then scene = ModeSelectScene() - elseif (e.scancode == config.input.retry) then - -- fuck this, this is hacky but the way this codebase is setup prevents anything else - -- it seems like all the values that get touched in the child gamemode class - -- stop being linked to the values of the GameMode superclass because of how `mt.__index` works - -- 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. - love.keypressed("escape", "escape", false) - love.keypressed("return", "return", false) - elseif e.scancode == "escape" then - scene = ModeSelectScene() + elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then + self.inputs[e.input] = true + end +end + +function GameScene:onInputRelease(e) + if e.input and string.sub(e.input, 1, 5) ~= "menu_" then + self.inputs[e.input] = false end end diff --git a/scene/game_config.lua b/scene/game_config.lua index f1edd74..b921dd3 100644 --- a/scene/game_config.lua +++ b/scene/game_config.lua @@ -5,10 +5,10 @@ ConfigScene.title = "Game Settings" require 'load.save' ConfigScene.options = { - -- this serves as reference to what the options' values mean i guess? - {"manlock", "Manual locking", {"Per ruleset","Per gamemode","Harddrop", "Softdrop"}}, - {"piece_colour", "Piece Colours", {"Per ruleset", "Arika", "TTC"}}, - {"world_reverse", "World Reverse", {"No", "Yes"}}, + -- this serves as reference to what the options' values mean i guess? + {"manlock", "Manual locking",{"Per ruleset","Per gamemode","Harddrop", "Softdrop"}}, + {"piece_colour", "Piece Colours", {"Per ruleset","Arika" ,"TTC"}}, + {"world_reverse","A Button Rotation", {"Left" ,"Auto" ,"Right"}}, } local optioncount = #ConfigScene.options @@ -16,11 +16,11 @@ function ConfigScene:new() -- load current config self.config = config.input self.highlight = 1 - + DiscordRPC:update({ - details = "In menus", - state = "Changing game settings", - }) + details = "In menus", + state = "Changing game settings", + }) end function ConfigScene:update() @@ -33,46 +33,46 @@ function ConfigScene:render() 0, 0, 0, 0.5, 0.5 ) - + 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.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) for i, option in ipairs(ConfigScene.options) do - love.graphics.setColor(1, 1, 1, 1) - love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left") - 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.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center") - end + love.graphics.setColor(1, 1, 1, 1) + love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left") + 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.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center") + end end end -function ConfigScene:onKeyPress(e) - if e.scancode == "return" and e.isRepeat == false then +function ConfigScene:onInputPress(e) + if e.input == "menu_decide" or e.scancode == "return" then playSE("mode_decide") saveConfig() 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") 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") 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") - local option = ConfigScene.options[self.highlight] - 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 + local option = ConfigScene.options[self.highlight] + config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3]) + elseif e.input == "right" or e.scancode == "right" then playSE("cursor_lr") - local option = ConfigScene.options[self.highlight] - config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3]) - elseif e.scancode == "escape" then - loadSave() - scene = TitleScene() + local option = ConfigScene.options[self.highlight] + config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3]) + elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then + loadSave() + scene = TitleScene() end end diff --git a/scene/input_config.lua b/scene/input_config.lua index 6170d9f..a389382 100644 --- a/scene/input_config.lua +++ b/scene/input_config.lua @@ -5,28 +5,38 @@ ConfigScene.title = "Input Config" require 'load.save' local configurable_inputs = { + "menu_decide", + "menu_back", "left", - "right", - "up", - "down", + "right", + "up", + "down", "rotate_left", "rotate_left2", - "rotate_right", - "rotate_right2", - "rotate_180", - "hold", - "retry", + "rotate_right", + "rotate_right2", + "rotate_180", + "hold", + "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() - -- load current config - self.config = config.input self.input_state = 1 + self.set_inputs = newSetInputs() + self.new_input = {} DiscordRPC:update({ - details = "In menus", - state = "Changing input config", - }) + details = "In menus", + state = "Changing input config", + }) end function ConfigScene:update() @@ -41,39 +51,106 @@ function ConfigScene:render() ) love.graphics.setFont(font_3x5_2) - for i, input in pairs(configurable_inputs) do - love.graphics.printf(input, 40, 50 + i * 20, 200, "left") - if config.input[input] then - love.graphics.printf( - love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")", - 240, 50 + i * 20, 200, "left" - ) + for i, input in ipairs(configurable_inputs) do + love.graphics.printf(input, 40, 50 + i * 20, 200, "left") + if self.set_inputs[input] then + love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left") end end 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 - 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 -function ConfigScene:onKeyPress(e) - if self.input_state > table.getn(configurable_inputs) then - if e.scancode == "return" then - -- save, then load next scene - saveConfig() +local function addJoystick(input, name) + if not input.joysticks then + input.joysticks = {} + end + 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() - elseif e.scancode == "delete" or e.scancode == "backspace" then - self.input_state = 1 + elseif self.input_state > table.getn(configurable_inputs) then + 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 - 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 diff --git a/scene/mode_select.lua b/scene/mode_select.lua index 54ded09..1ffe0f0 100755 --- a/scene/mode_select.lua +++ b/scene/mode_select.lua @@ -5,44 +5,6 @@ ModeSelectScene.title = "Game Start" current_mode = 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() self.menu_state = { mode = current_mode, @@ -50,9 +12,9 @@ function ModeSelectScene:new() select = "mode", } DiscordRPC:update({ - details = "In menus", - state = "Choosing a mode", - }) + details = "In menus", + state = "Choosing a mode", + }) end function ModeSelectScene:update() @@ -70,14 +32,14 @@ function ModeSelectScene:render() elseif self.menu_state.select == "ruleset" then love.graphics.setColor(1, 1, 1, 0.25) end - love.graphics.rectangle("fill", 20, 78 + 20 * self.menu_state.mode, 240, 22) + love.graphics.rectangle("fill", 20, 258, 240, 22) if self.menu_state.select == "mode" then love.graphics.setColor(1, 1, 1, 0.25) elseif self.menu_state.select == "ruleset" then love.graphics.setColor(1, 1, 1, 0.5) end - love.graphics.rectangle("fill", 340, 78 + 20 * self.menu_state.ruleset, 200, 22) + love.graphics.rectangle("fill", 340, 258, 200, 22) love.graphics.setColor(1, 1, 1, 1) @@ -85,15 +47,19 @@ function ModeSelectScene:render() love.graphics.setFont(font_3x5_2) 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 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 -function ModeSelectScene:onKeyPress(e) - if e.scancode == "return" and e.isRepeat == false then +function ModeSelectScene:onInputPress(e) + if e.input == "menu_decide" or e.scancode == "return" then current_mode = self.menu_state.mode current_ruleset = self.menu_state.ruleset config.current_mode = current_mode @@ -101,18 +67,17 @@ function ModeSelectScene:onKeyPress(e) playSE("mode_decide") saveConfig() 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) 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) playSE("cursor") - elseif (e.scancode == config.input["left"] or e.scancode == "left") or - (e.scancode == config.input["right"] or e.scancode == "right") then + elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then self:switchSelect() playSE("cursor_lr") - elseif e.scancode == "escape" then - scene = TitleScene() + elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then + scene = TitleScene() end end diff --git a/scene/title.lua b/scene/title.lua index adcb688..3c6b48a 100644 --- a/scene/title.lua +++ b/scene/title.lua @@ -4,30 +4,30 @@ local main_menu_screens = { ModeSelectScene, InputConfigScene, GameConfigScene, - ExitScene, + ExitScene, } local mainmenuidle = { - "Idle", - "On title screen", - "On main menu screen", - "Twiddling their thumbs", - "Admiring the main menu's BG", - "Waiting for spring to come", - "Actually not playing", - "Contemplating collecting stars", - "Preparing to put the block!!", - "Having a nap", - "In menus", - "Bottom text", + "Idle", + "On title screen", + "On main menu screen", + "Twiddling their thumbs", + "Admiring the main menu's BG", + "Waiting for spring to come", + "Actually not playing", + "Contemplating collecting stars", + "Preparing to put the block!!", + "Having a nap", + "In menus", + "Bottom text", } function TitleScene:new() self.main_menu_state = 1 DiscordRPC:update({ - details = "In menus", - state = mainmenuidle[math.random(#mainmenuidle)], - }) + details = "In menus", + state = mainmenuidle[math.random(#mainmenuidle)], + }) end function TitleScene:update() @@ -57,18 +57,18 @@ function TitleScene:changeOption(rel) self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1 end -function TitleScene:onKeyPress(e) - if e.scancode == "return" and e.isRepeat == false then +function TitleScene:onInputPress(e) + if e.input == "menu_decide" or e.scancode == "return" then playSE("main_decide") 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) 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) playSE("cursor") - elseif e.scancode == "escape" and e.isRepeat == false then - love.event.quit() + elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then + love.event.quit() end end diff --git a/tetris/components/grid.lua b/tetris/components/grid.lua index 68fab09..d1702ed 100644 --- a/tetris/components/grid.lua +++ b/tetris/components/grid.lua @@ -4,6 +4,7 @@ local Grid = Object:extend() local empty = { skin = "", colour = "" } local oob = { skin = "", colour = "" } +local block = { skin = "2tie", colour = "A" } function Grid:new() self.grid = {} @@ -28,10 +29,10 @@ function Grid:clear() end function Grid:getCell(x, y) - if x < 1 or x > 10 or y > 24 then return oob - elseif y < 1 then return empty - else return self.grid[y][x] - end + if x < 1 or x > 10 or y > 24 then return oob + elseif y < 1 then return empty + else return self.grid[y][x] + end end function Grid:isOccupied(x, y) @@ -66,11 +67,11 @@ function Grid:canPlaceBigPiece(piece) for index, offset in pairs(offsets) do local x = piece.position.x + offset.x local y = piece.position.y + offset.y - if ( - self:isOccupied(x * 2 + 0, 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 + 1, y * 2 + 1) + if ( + self:isOccupied(x * 2 + 0, 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 + 1, y * 2 + 1) ) then return false end @@ -141,14 +142,35 @@ function Grid:copyBottomRow() self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty} self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} for col = 1, 10 do - self.grid[24][col] = (self.grid[23][col] == empty) and empty or { - skin = self.grid[23][col].skin, - colour = "X" - } + self.grid[24][col] = (self.grid[23][col] == empty) and empty or block end return true end +function Grid:garbageRise(row_vals) + for row = 1, 23 do + self.grid[row] = self.grid[row+1] + self.grid_age[row] = self.grid_age[row+1] + end + self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty} + self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + for col = 1, 10 do + self.grid[24][col] = (row_vals[col] == "e") and empty or block + 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) if piece.big then self:applyBigPiece(piece) @@ -174,12 +196,12 @@ function Grid:applyBigPiece(piece) y = piece.position.y + offset.y for a = 1, 2 do for b = 1, 2 do - if y*2+a > 0 then - self.grid[y*2+a][x*2+b] = { - skin = piece.skin, - colour = piece.colour - } - end + if y*2+a > 0 then + self.grid[y*2+a][x*2+b] = { + skin = piece.skin, + colour = piece.colour + } + end end end end @@ -187,45 +209,45 @@ end function Grid:checkForBravo(cleared_row_count) for i = 0, 23 - cleared_row_count do - for j = 0, 9 do - if self:isOccupied(j, i) then return false end - end - end + for j = 0, 9 do + if self:isOccupied(j, i) then return false end + end + end return true end function Grid:checkSecretGrade() - local sgrade = 0 - for i=23,5,-1 do - local validLine = true - local emptyCell = 0 - if i > 13 then - emptyCell = 23-i - end - if i <= 13 then - emptyCell = i-5 - end - for j=0,9 do - if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then - validLine = false - end - end - if not self:isOccupied(emptyCell,i-1) then - validLine = false - end - if(validLine) then - sgrade = sgrade + 1 - else - -- return sgrade - end - end - --[[ - if(sgrade == 0) then return "" - elseif(sgrade < 10) then return 10-sgrade - elseif(sgrade < 19) then return "S"..(sgrade-9) end - return "GM" - --]] - return sgrade + local sgrade = 0 + for i=23,5,-1 do + local validLine = true + local emptyCell = 0 + if i > 13 then + emptyCell = 23-i + end + if i <= 13 then + emptyCell = i-5 + end + for j=0,9 do + if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then + validLine = false + end + end + if not self:isOccupied(emptyCell,i-1) then + validLine = false + end + if(validLine) then + sgrade = sgrade + 1 + else + return sgrade + end + end + --[[ + if(sgrade == 0) then return "" + elseif(sgrade < 10) then return 10-sgrade + elseif(sgrade < 19) then return "S"..(sgrade-9) end + return "GM" + --]] + return sgrade end function Grid:update() @@ -239,14 +261,18 @@ function Grid:update() end function Grid:draw() - for y = 1, 24 do + for y = 5, 24 do for x = 1, 10 do 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.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16) 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) end if self.grid[y][x].skin ~= "bone" then @@ -271,12 +297,12 @@ function Grid:draw() end function Grid:drawInvisible(opacity_function, garbage_opacity_function) - for y = 1, 24 do + for y = 5, 24 do for x = 1, 10 do if self.grid[y][x] ~= empty then if self.grid[y][x].colour == "X" then 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]) else opacity = opacity_function(self.grid_age[y][x]) diff --git a/tetris/components/piece.lua b/tetris/components/piece.lua index 15c0a76..1ae0790 100644 --- a/tetris/components/piece.lua +++ b/tetris/components/piece.lua @@ -10,7 +10,7 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay self.gravity = gravity self.lock_delay = lock_delay self.skin = skin - self.colour = colour + self.colour = colour self.ghost = false self.locked = false self.big = big @@ -98,7 +98,7 @@ end function Piece:dropToBottom(grid) local piece_y = self.position.y - self:dropSquares(24, grid) + self:dropSquares(math.huge, grid) self.gravity = 0 if self.position.y > piece_y then -- if it got dropped any, also reset lock delay diff --git a/tetris/modes/big_a2.lua b/tetris/modes/big_a2.lua index 86c5317..dfea465 100755 --- a/tetris/modes/big_a2.lua +++ b/tetris/modes/big_a2.lua @@ -15,99 +15,95 @@ MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible r function MarathonA2Game:new() - self.super:new() - self.big_mode = true + self.super:new() + self.big_mode = true self.roll_frames = 0 - self.combo = 1 - self.randomizer = History6RollsRandomizer() + self.combo = 1 + self.grade = 0 self.grade_points = 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.lock_drop = false + self.lock_drop = false + self.lock_hard_drop = false self.enable_hold = false self.next_queue_length = 1 end function MarathonA2Game:getARE() - if self.level < 700 then return 27 - elseif self.level < 800 then return 18 - else return 14 end + if self.level < 700 then return 27 + elseif self.level < 800 then return 18 + else return 14 end end function MarathonA2Game:getLineARE() - if self.level < 600 then return 27 - elseif self.level < 700 then return 18 - elseif self.level < 800 then return 14 - else return 8 end + if self.level < 600 then return 27 + elseif self.level < 700 then return 18 + elseif self.level < 800 then return 14 + else return 8 end end function MarathonA2Game:getDasLimit() - if self.level < 500 then return 15 - elseif self.level < 900 then return 9 - else return 7 end + if self.level < 500 then return 15 + elseif self.level < 900 then return 9 + else return 7 end end function MarathonA2Game:getLineClearDelay() - if self.level < 500 then return 40 - elseif self.level < 600 then return 25 - elseif self.level < 700 then return 16 - elseif self.level < 800 then return 12 - else return 6 end + if self.level < 500 then return 40 + elseif self.level < 600 then return 25 + elseif self.level < 700 then return 16 + elseif self.level < 800 then return 12 + else return 6 end end function MarathonA2Game:getLockDelay() - if self.level < 900 then return 30 - else return 17 end + if self.level < 900 then return 30 + else return 17 end end function MarathonA2Game:getGravity() - if (self.level < 30) then return 4/256 - elseif (self.level < 35) then return 6/256 - elseif (self.level < 40) then return 8/256 - elseif (self.level < 50) then return 10/256 - elseif (self.level < 60) then return 12/256 - elseif (self.level < 70) then return 16/256 - elseif (self.level < 80) then return 32/256 - elseif (self.level < 90) then return 48/256 - elseif (self.level < 100) then return 64/256 - elseif (self.level < 120) then return 80/256 - elseif (self.level < 140) then return 96/256 - elseif (self.level < 160) then return 112/256 - elseif (self.level < 170) then return 128/256 - elseif (self.level < 200) then return 144/256 - elseif (self.level < 220) then return 4/256 - elseif (self.level < 230) then return 32/256 - elseif (self.level < 233) then return 64/256 - elseif (self.level < 236) then return 96/256 - elseif (self.level < 239) then return 128/256 - elseif (self.level < 243) then return 160/256 - elseif (self.level < 247) then return 192/256 - elseif (self.level < 251) then return 224/256 - elseif (self.level < 300) then return 1 - elseif (self.level < 330) then return 2 - elseif (self.level < 360) then return 3 - elseif (self.level < 400) then return 4 - elseif (self.level < 420) then return 5 - elseif (self.level < 450) then return 4 - elseif (self.level < 500) then return 3 - else return 20 - end + if (self.level < 30) then return 4/256 + elseif (self.level < 35) then return 6/256 + elseif (self.level < 40) then return 8/256 + elseif (self.level < 50) then return 10/256 + elseif (self.level < 60) then return 12/256 + elseif (self.level < 70) then return 16/256 + elseif (self.level < 80) then return 32/256 + elseif (self.level < 90) then return 48/256 + elseif (self.level < 100) then return 64/256 + elseif (self.level < 120) then return 80/256 + elseif (self.level < 140) then return 96/256 + elseif (self.level < 160) then return 112/256 + elseif (self.level < 170) then return 128/256 + elseif (self.level < 200) then return 144/256 + elseif (self.level < 220) then return 4/256 + elseif (self.level < 230) then return 32/256 + elseif (self.level < 233) then return 64/256 + elseif (self.level < 236) then return 96/256 + elseif (self.level < 239) then return 128/256 + elseif (self.level < 243) then return 160/256 + elseif (self.level < 247) then return 192/256 + elseif (self.level < 251) then return 224/256 + elseif (self.level < 300) then return 1 + elseif (self.level < 330) then return 2 + elseif (self.level < 360) then return 3 + elseif (self.level < 400) then return 4 + elseif (self.level < 420) then return 5 + elseif (self.level < 450) then return 4 + elseif (self.level < 500) then return 3 + else return 20 + end end function MarathonA2Game:advanceOneFrame() if self.clear then self.roll_frames = self.roll_frames + 1 + if self.roll_frames < 0 then return false end if self.roll_frames > 3694 then self.completed = true - if self.grade == 32 then - self.grade = 33 - end end elseif self.ready_frames == 0 then self.frames = self.frames + 1 @@ -123,43 +119,31 @@ end function MarathonA2Game:onLineClear(cleared_row_count) 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) - if self.level == 999 and not self.clear then - self.clear = true - if self:qualifiesForMRoll() then - self.grade = 32 - end - self.grid:clear() - self.roll_frames = -150 - end + self.level = math.min(self.level + cleared_row_count, 999) + if self.level == 999 and not self.clear then + self.clear = true + self.grid:clear() + self.roll_frames = -150 + end + self.lock_drop = self.level >= 900 + self.lock_hard_drop = self.level >= 900 end function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) - if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end - cleared_lines = cleared_lines / 2 - self:updateGrade(cleared_lines) - if cleared_lines > 0 then - self.score = self.score + ( - (math.ceil((level + cleared_lines) / 4) + 2 * drop_bonus) * - cleared_lines * self.combo * self.bravo - ) - self.lines = self.lines + cleared_lines - self.combo = self.combo + 2 * (cleared_lines - 1) - else + if not self.clear then + cleared_lines = cleared_lines / 2 + self:updateGrade(cleared_lines) + if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo * self.bravo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - 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 @@ -224,7 +208,7 @@ local grade_conversion = { 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 18, 19 + 17 } function MarathonA2Game:updateGrade(cleared_lines) @@ -249,49 +233,12 @@ function MarathonA2Game:updateGrade(cleared_lines) 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() local grade = grade_conversion[self.grade] if grade < 9 then return tostring(9 - grade) elseif grade < 18 then return "S" .. tostring(grade - 8) - elseif grade == 18 then - return "M" - else - return "GM" end end @@ -301,18 +248,9 @@ MarathonA2Game.rollOpacityFunction = function(age) else return 1 - (age - 240) / 60 end end -MarathonA2Game.mRollOpacityFunction = function(age) - if age > 4 then return 0 - else return 1 - age / 4 end -end - function MarathonA2Game:drawGrid(ruleset) if self.clear and not (self.completed or self.game_over) then - if self:qualifiesForMRoll() then - self.grid:drawInvisible(self.mRollOpacityFunction) - else - self.grid:drawInvisible(self.rollOpacityFunction) - end + self.grid:drawInvisible(self.rollOpacityFunction) else self.grid:draw() 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.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.setColor(1, 1, 1, 1) love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") diff --git a/tetris/modes/ck.lua b/tetris/modes/ck.lua deleted file mode 100644 index b9090be..0000000 --- a/tetris/modes/ck.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/demon_mode.lua b/tetris/modes/demon_mode.lua deleted file mode 100644 index 9a8eac5..0000000 --- a/tetris/modes/demon_mode.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/gamemode.lua b/tetris/modes/gamemode.lua index f5ce7c8..9648f2c 100644 --- a/tetris/modes/gamemode.lua +++ b/tetris/modes/gamemode.lua @@ -43,12 +43,12 @@ function GameMode:new() self.draw_section_times = false self.draw_secondary_section_times = false self.big_mode = false - self.rpc_details = "In game" + self.rpc_details = "In game" -- variables related to configurable parameters self.drop_locked = false self.hard_drop_locked = false - self.lock_on_soft_drop = false - self.lock_on_hard_drop = false + self.lock_on_soft_drop = false + self.lock_on_hard_drop = false self.hold_queue = nil self.held = false self.section_start_time = 0 @@ -65,7 +65,7 @@ function GameMode:getLineClearDelay() return 40 end function GameMode:getDasLimit() return 15 end function GameMode:getNextPiece(ruleset) - + return { skin = "2tie", shape = self.randomizer:nextPiece(), @@ -75,12 +75,12 @@ end function GameMode:initialize(ruleset) -- generate next queue - self:new() + self:new() for i = 1, self.next_queue_length do table.insert(self.next_queue, self:getNextPiece(ruleset)) end - 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_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] end function GameMode:update(inputs, ruleset) @@ -369,7 +369,7 @@ function GameMode:drawGhostPiece(ruleset) end 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) for index, offset in pairs(offsets) do local x = offset.x + ruleset.spawn_positions[piece].x @@ -389,7 +389,8 @@ function GameMode:drawNextQueue(ruleset) end end if self.hold_queue ~= nil then - self:setHoldOpacity() + local hold_color = self.held and 0.6 or 1 + self:setHoldOpacity(1, hold_color) drawPiece( self.hold_queue.shape, self.hold_queue.skin, @@ -400,8 +401,16 @@ function GameMode:drawNextQueue(ruleset) return false end -function GameMode:setNextOpacity(i) love.graphics.setColor(1, 1, 1, 1) end -function GameMode:setHoldOpacity() love.graphics.setColor(1, 1, 1, 1) end +function GameMode:setNextOpacity(i, j) + i = i ~= nil and i or 1 + j = j ~= nil and j or 1 + love.graphics.setColor(j, j, j, i) +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() love.graphics.setColor(1, 1, 1, 1) diff --git a/tetris/modes/interval_training.lua b/tetris/modes/interval_training.lua deleted file mode 100644 index 9d5c4ab..0000000 --- a/tetris/modes/interval_training.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/konoha.lua b/tetris/modes/konoha.lua deleted file mode 100755 index 009ab2c..0000000 --- a/tetris/modes/konoha.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/marathon_2020.lua b/tetris/modes/marathon_2020.lua index 4b61c14..c54cb72 100644 --- a/tetris/modes/marathon_2020.lua +++ b/tetris/modes/marathon_2020.lua @@ -36,10 +36,12 @@ function Marathon2020Game:new() self.grade_points = 0 self.grade_point_decay_counter = 0 self.max_grade_points = 0 + + self.cool_timer = 0 end 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 < 3 then return 21 elseif self.delay_level < 4 then return 18 @@ -58,7 +60,7 @@ function Marathon2020Game:getLineARE() end 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 < 5 then return 9 elseif self.delay_level < 8 then return 8 @@ -70,7 +72,7 @@ function Marathon2020Game:getDasLimit() end 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 < 4 then return 20 elseif self.delay_level < 5 then return 15 @@ -82,7 +84,7 @@ function Marathon2020Game:getLineClearDelay() end 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 < 8 then return 22 elseif self.delay_level < 9 then return 19 @@ -96,35 +98,35 @@ function Marathon2020Game:getLockDelay() end 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 < 40 then return 8/256 - elseif self.level < 50 then return 10/256 - elseif self.level < 60 then return 12/256 - elseif self.level < 70 then return 16/256 - elseif self.level < 80 then return 32/256 - elseif self.level < 90 then return 48/256 - elseif self.level < 100 then return 64/256 - elseif self.level < 120 then return 80/256 - elseif self.level < 140 then return 96/256 - elseif self.level < 160 then return 112/256 - elseif self.level < 170 then return 128/256 - elseif self.level < 200 then return 144/256 - elseif self.level < 220 then return 4/256 - elseif self.level < 230 then return 32/256 - elseif self.level < 233 then return 64/256 - elseif self.level < 236 then return 96/256 - elseif self.level < 239 then return 128/256 - elseif self.level < 243 then return 160/256 - elseif self.level < 247 then return 192/256 - elseif self.level < 251 then return 224/256 + elseif self.level < 40 then return 8/256 + elseif self.level < 50 then return 10/256 + elseif self.level < 60 then return 12/256 + elseif self.level < 70 then return 16/256 + elseif self.level < 80 then return 32/256 + elseif self.level < 90 then return 48/256 + elseif self.level < 100 then return 64/256 + elseif self.level < 120 then return 80/256 + elseif self.level < 140 then return 96/256 + elseif self.level < 160 then return 112/256 + elseif self.level < 170 then return 128/256 + elseif self.level < 200 then return 144/256 + elseif self.level < 220 then return 4/256 + elseif self.level < 230 then return 32/256 + elseif self.level < 233 then return 64/256 + elseif self.level < 236 then return 96/256 + elseif self.level < 239 then return 128/256 + elseif self.level < 243 then return 160/256 + elseif self.level < 247 then return 192/256 + elseif self.level < 251 then return 224/256 elseif self.level < 300 then return 1 - elseif self.level < 330 then return 2 - elseif self.level < 360 then return 3 - elseif self.level < 400 then return 4 - elseif self.level < 420 then return 5 - elseif self.level < 450 then return 4 - elseif self.level < 500 then return 3 + elseif self.level < 330 then return 2 + elseif self.level < 360 then return 3 + elseif self.level < 400 then return 4 + elseif self.level < 420 then return 5 + elseif self.level < 450 then return 4 + elseif self.level < 500 then return 3 else return 20 end end @@ -258,7 +260,7 @@ local function getSectionForLevel(level) end 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 section == 20 then return 2020 else return section * 100 end @@ -301,7 +303,7 @@ function Marathon2020Game:checkClear(level) level >= 2020 ) 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[1000] == false then self.level = 1000 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.delay_level = math.min(20, self.delay_level + 1) table.insert(self.section_status, "cool") + self.cool_timer = 300 end local section = getSectionForLevel(old_level) @@ -430,6 +433,11 @@ function Marathon2020Game:drawScoringInfo() 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.printf(self:getTotalGrade(), text_x, 120, 90, "left") love.graphics.printf(self.grade_points, text_x, 220, 90, "left") diff --git a/tetris/modes/marathon_a1.lua b/tetris/modes/marathon_a1.lua index daa5086..7fc5b71 100644 --- a/tetris/modes/marathon_a1.lua +++ b/tetris/modes/marathon_a1.lua @@ -14,26 +14,26 @@ MarathonA1Game.tagline = "Can you score enough points to reach the title of Gran function MarathonA1Game:new() - MarathonA1Game.super:new() - + MarathonA1Game.super:new() + self.roll_frames = 0 - self.combo = 1 + self.combo = 1 self.bravos = 0 self.gm_conditions = { level300 = false, level500 = false, level999 = false - } - self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } - + } + self.SGnames = { + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } + self.randomizer = History4RollsRandomizer() - self.lock_drop = false - self.enable_hard_drop = false + self.lock_drop = false + self.enable_hard_drop = false self.enable_hold = false self.next_queue_length = 1 end @@ -59,60 +59,60 @@ function MarathonA1Game:getLockDelay() end local function getRankForScore(score) - if score < 400 then return {rank = "9", next = 400} - elseif score < 800 then return {rank = "8", next = 800} - elseif score < 1400 then return {rank = "7", next = 1400} - elseif score < 2000 then return {rank = "6", next = 2000} - elseif score < 3500 then return {rank = "5", next = 3500} - elseif score < 5500 then return {rank = "4", next = 5500} - elseif score < 8000 then return {rank = "3", next = 8000} - elseif score < 12000 then return {rank = "2", next = 12000} - elseif score < 16000 then return {rank = "1", next = 16000} - elseif score < 22000 then return {rank = "S1", next = 22000} - elseif score < 30000 then return {rank = "S2", next = 30000} - elseif score < 40000 then return {rank = "S3", next = 40000} - elseif score < 52000 then return {rank = "S4", next = 52000} - elseif score < 66000 then return {rank = "S5", next = 66000} - elseif score < 82000 then return {rank = "S6", next = 82000} - elseif score < 100000 then return {rank = "S7", next = 100000} - elseif score < 120000 then return {rank = "S8", next = 120000} - else return {rank = "S9", next = "???"} - end + if score < 400 then return {rank = "9", next = 400} + elseif score < 800 then return {rank = "8", next = 800} + elseif score < 1400 then return {rank = "7", next = 1400} + elseif score < 2000 then return {rank = "6", next = 2000} + elseif score < 3500 then return {rank = "5", next = 3500} + elseif score < 5500 then return {rank = "4", next = 5500} + elseif score < 8000 then return {rank = "3", next = 8000} + elseif score < 12000 then return {rank = "2", next = 12000} + elseif score < 16000 then return {rank = "1", next = 16000} + elseif score < 22000 then return {rank = "S1", next = 22000} + elseif score < 30000 then return {rank = "S2", next = 30000} + elseif score < 40000 then return {rank = "S3", next = 40000} + elseif score < 52000 then return {rank = "S4", next = 52000} + elseif score < 66000 then return {rank = "S5", next = 66000} + elseif score < 82000 then return {rank = "S6", next = 82000} + elseif score < 100000 then return {rank = "S7", next = 100000} + elseif score < 120000 then return {rank = "S8", next = 120000} + else return {rank = "S9", next = "???"} + end end function MarathonA1Game:getGravity() - local level = self.level - if (level < 30) then return 4/256 - elseif (level < 35) then return 6/256 - elseif (level < 40) then return 8/256 - elseif (level < 50) then return 10/256 - elseif (level < 60) then return 12/256 - elseif (level < 70) then return 16/256 - elseif (level < 80) then return 32/256 - elseif (level < 90) then return 48/256 - elseif (level < 100) then return 64/256 - elseif (level < 120) then return 80/256 - elseif (level < 140) then return 96/256 - elseif (level < 160) then return 112/256 - elseif (level < 170) then return 128/256 - elseif (level < 200) then return 144/256 - elseif (level < 220) then return 4/256 - elseif (level < 230) then return 32/256 - elseif (level < 233) then return 64/256 - elseif (level < 236) then return 96/256 - elseif (level < 239) then return 128/256 - elseif (level < 243) then return 160/256 - elseif (level < 247) then return 192/256 - elseif (level < 251) then return 224/256 - elseif (level < 300) then return 1 - elseif (level < 330) then return 2 - elseif (level < 360) then return 3 - elseif (level < 400) then return 4 - elseif (level < 420) then return 5 - elseif (level < 450) then return 4 - elseif (level < 500) then return 3 - else return 20 - end + local level = self.level + if (level < 30) then return 4/256 + elseif (level < 35) then return 6/256 + elseif (level < 40) then return 8/256 + elseif (level < 50) then return 10/256 + elseif (level < 60) then return 12/256 + elseif (level < 70) then return 16/256 + elseif (level < 80) then return 32/256 + elseif (level < 90) then return 48/256 + elseif (level < 100) then return 64/256 + elseif (level < 120) then return 80/256 + elseif (level < 140) then return 96/256 + elseif (level < 160) then return 112/256 + elseif (level < 170) then return 128/256 + elseif (level < 200) then return 144/256 + elseif (level < 220) then return 4/256 + elseif (level < 230) then return 32/256 + elseif (level < 233) then return 64/256 + elseif (level < 236) then return 96/256 + elseif (level < 239) then return 128/256 + elseif (level < 243) then return 160/256 + elseif (level < 247) then return 192/256 + elseif (level < 251) then return 224/256 + elseif (level < 300) then return 1 + elseif (level < 330) then return 2 + elseif (level < 360) then return 3 + elseif (level < 400) then return 4 + elseif (level < 420) then return 5 + elseif (level < 450) then return 4 + elseif (level < 500) then return 3 + else return 20 + end end function MarathonA1Game:advanceOneFrame() @@ -134,47 +134,47 @@ function MarathonA1Game:onPieceEnter() end 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 local new_level = math.min(self.level + cleared_row_count, 999) - if self.level == 999 then + if new_level == 999 then self.clear = true - else - self.level = new_level end + self.level = new_level end end function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines) - if self.grid:checkForBravo(cleared_lines) then - self.bravo = 4 - self.bravos = self.bravos + 1 - else self.bravo = 1 end - if cleared_lines > 0 then - self.combo = self.combo + (cleared_lines - 1) * 2 - self.score = self.score + ( - (math.ceil((level + cleared_lines) / 4) + drop_bonus) * - cleared_lines * self.combo * self.bravo - ) - self.lines = self.lines + cleared_lines - else + if not self.clear then + if self.grid:checkForBravo(cleared_lines) then + self.bravo = 4 + self.bravos = self.bravos + 1 + else self.bravo = 1 end + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo * self.bravo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end function MarathonA1Game:checkGMRequirements(old_level, new_level) if old_level < 300 and new_level >= 300 then - if self.score > 12000 and self.frames <= frameTime(4,15) then + if self.score >= 12000 and self.frames <= frameTime(4,15) then self.gm_conditions["level300"] = true end elseif old_level < 500 and new_level >= 500 then - if self.score > 40000 and self.frames <= frameTime(7,30) then + if self.score >= 40000 and self.frames <= frameTime(7,30) then self.gm_conditions["level500"] = true end elseif old_level < 999 and new_level >= 999 then - if self.score > 126000 and self.frames <= frameTime(13,30) then - self.gm_conditions["level900"] = true + if self.score >= 126000 and self.frames <= frameTime(13,30) then + self.gm_conditions["level999"] = true end end end @@ -201,16 +201,16 @@ function MarathonA1Game:drawScoringInfo() love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("NEXT RANK", 240, 260, 90, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end love.graphics.setFont(font_3x5_3) love.graphics.printf(self.score, 240, 220, 90, "left") - if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["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") else love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left") @@ -218,11 +218,11 @@ function MarathonA1Game:drawScoringInfo() love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left") love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end - + love.graphics.setFont(font_8x11) love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") end diff --git a/tetris/modes/marathon_a2.lua b/tetris/modes/marathon_a2.lua index 601dc2e..5986584 100644 --- a/tetris/modes/marathon_a2.lua +++ b/tetris/modes/marathon_a2.lua @@ -15,10 +15,10 @@ MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible r function MarathonA2Game:new() - MarathonA2Game.super:new() - + MarathonA2Game.super:new() + self.roll_frames = 0 - self.combo = 1 + self.combo = 1 self.randomizer = History6RollsRandomizer() self.grade = 0 self.grade_points = 0 @@ -26,89 +26,89 @@ function MarathonA2Game:new() self.section_start_time = 0 self.section_times = { [0] = 0 } self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } - - self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } - - self.randomizer = History6RollsRandomizer() + + self.SGnames = { + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } - self.lock_drop = false + self.lock_drop = false + self.lock_hard_drop = false self.enable_hold = false self.next_queue_length = 1 end function MarathonA2Game:getARE() - if self.level < 700 then return 27 - elseif self.level < 800 then return 18 - else return 14 end + if self.level < 700 then return 27 + elseif self.level < 800 then return 18 + else return 14 end end function MarathonA2Game:getLineARE() - if self.level < 600 then return 27 - elseif self.level < 700 then return 18 - elseif self.level < 800 then return 14 - else return 8 end + if self.level < 600 then return 27 + elseif self.level < 700 then return 18 + elseif self.level < 800 then return 14 + else return 8 end end function MarathonA2Game:getDasLimit() - if self.level < 500 then return 15 - elseif self.level < 900 then return 9 - else return 7 end + if self.level < 500 then return 15 + elseif self.level < 900 then return 9 + else return 7 end end function MarathonA2Game:getLineClearDelay() - if self.level < 500 then return 40 - elseif self.level < 600 then return 25 - elseif self.level < 700 then return 16 - elseif self.level < 800 then return 12 - else return 6 end + if self.level < 500 then return 40 + elseif self.level < 600 then return 25 + elseif self.level < 700 then return 16 + elseif self.level < 800 then return 12 + else return 6 end end function MarathonA2Game:getLockDelay() - if self.level < 900 then return 30 - else return 17 end + if self.level < 900 then return 30 + else return 17 end end function MarathonA2Game:getGravity() - if (self.level < 30) then return 4/256 - elseif (self.level < 35) then return 6/256 - elseif (self.level < 40) then return 8/256 - elseif (self.level < 50) then return 10/256 - elseif (self.level < 60) then return 12/256 - elseif (self.level < 70) then return 16/256 - elseif (self.level < 80) then return 32/256 - elseif (self.level < 90) then return 48/256 - elseif (self.level < 100) then return 64/256 - elseif (self.level < 120) then return 80/256 - elseif (self.level < 140) then return 96/256 - elseif (self.level < 160) then return 112/256 - elseif (self.level < 170) then return 128/256 - elseif (self.level < 200) then return 144/256 - elseif (self.level < 220) then return 4/256 - elseif (self.level < 230) then return 32/256 - elseif (self.level < 233) then return 64/256 - elseif (self.level < 236) then return 96/256 - elseif (self.level < 239) then return 128/256 - elseif (self.level < 243) then return 160/256 - elseif (self.level < 247) then return 192/256 - elseif (self.level < 251) then return 224/256 - elseif (self.level < 300) then return 1 - elseif (self.level < 330) then return 2 - elseif (self.level < 360) then return 3 - elseif (self.level < 400) then return 4 - elseif (self.level < 420) then return 5 - elseif (self.level < 450) then return 4 - elseif (self.level < 500) then return 3 - else return 20 - end + if (self.level < 30) then return 4/256 + elseif (self.level < 35) then return 6/256 + elseif (self.level < 40) then return 8/256 + elseif (self.level < 50) then return 10/256 + elseif (self.level < 60) then return 12/256 + elseif (self.level < 70) then return 16/256 + elseif (self.level < 80) then return 32/256 + elseif (self.level < 90) then return 48/256 + elseif (self.level < 100) then return 64/256 + elseif (self.level < 120) then return 80/256 + elseif (self.level < 140) then return 96/256 + elseif (self.level < 160) then return 112/256 + elseif (self.level < 170) then return 128/256 + elseif (self.level < 200) then return 144/256 + elseif (self.level < 220) then return 4/256 + elseif (self.level < 230) then return 32/256 + elseif (self.level < 233) then return 64/256 + elseif (self.level < 236) then return 96/256 + elseif (self.level < 239) then return 128/256 + elseif (self.level < 243) then return 160/256 + elseif (self.level < 247) then return 192/256 + elseif (self.level < 251) then return 224/256 + elseif (self.level < 300) then return 1 + elseif (self.level < 330) then return 2 + elseif (self.level < 360) then return 3 + elseif (self.level < 400) then return 4 + elseif (self.level < 420) then return 5 + elseif (self.level < 450) then return 4 + elseif (self.level < 500) then return 3 + else return 20 + end end function MarathonA2Game:advanceOneFrame() if self.clear then self.roll_frames = self.roll_frames + 1 + if self.roll_frames < 0 then return false end if self.roll_frames > 3694 then self.completed = true if self.grade == 32 then @@ -127,33 +127,33 @@ function MarathonA2Game:onPieceEnter() end end -function MarathonA2Game:onLineClear(cleared_row_count) - self:updateSectionTimes(self.level, self.level + cleared_row_count) - self.level = math.min(self.level + cleared_row_count, 999) - if self.level == 999 and not self.clear then - self.clear = true - if self:qualifiesForMRoll() then - self.grade = 32 - end - self.grid:clear() - self.roll_frames = -150 - end +function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) + if not self.clear then + self:updateGrade(cleared_lines) + if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo * self.bravo + ) + else + self.combo = 1 + end + self.drop_bonus = 0 + else self.lines = self.lines + cleared_lines end end -function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) - self:updateGrade(cleared_lines) - if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end - if cleared_lines > 0 then - self.score = self.score + ( - (math.ceil((level + cleared_lines) / 4) + 2 * drop_bonus) * - cleared_lines * self.combo * self.bravo - ) - self.lines = self.lines + cleared_lines - self.combo = self.combo + (cleared_lines - 1) * 2 - else - self.drop_bonus = 0 - self.combo = 1 +function MarathonA2Game:onLineClear(cleared_row_count) + self.level = math.min(self.level + cleared_row_count, 999) + if self.level == 999 and not self.clear then + self.clear = true + self.grid:clear() + if self:qualifiesForMRoll() then self.grade = 32 end + self.roll_frames = -150 end + self.lock_drop = self.level >= 900 + self.lock_hard_drop = self.level >= 900 end function MarathonA2Game:updateSectionTimes(old_level, new_level) @@ -253,7 +253,7 @@ function MarathonA2Game:updateGrade(cleared_lines) 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() if not self.clear then return false end @@ -280,7 +280,7 @@ function MarathonA2Game:qualifiesForMRoll() return false 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 end return true @@ -338,19 +338,29 @@ function MarathonA2Game:drawScoringInfo() love.graphics.printf("GRADE", 240, 120, 40, "left") love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end 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.setColor(1, 1, 1, 1) love.graphics.printf(self.score, 240, 220, 90, "left") love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end love.graphics.setFont(font_8x11) love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") diff --git a/tetris/modes/marathon_a3.lua b/tetris/modes/marathon_a3.lua index f1fea5e..a0b475c 100644 --- a/tetris/modes/marathon_a3.lua +++ b/tetris/modes/marathon_a3.lua @@ -15,11 +15,11 @@ MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all function MarathonA3Game:new() - MarathonA3Game.super:new() - - self.speed_level = 0 + MarathonA3Game.super:new() + + self.speed_level = 0 self.roll_frames = 0 - self.combo = 1 + self.combo = 1 self.grade = 0 self.grade_points = 0 self.roll_points = 0 @@ -29,96 +29,99 @@ function MarathonA3Game:new() self.section_start_time = 0 self.section_70_times = { [0] = 0 } self.section_times = { [0] = 0 } - + self.section_cool = false + self.randomizer = History6RollsRandomizer() self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } - self.lock_drop = true - self.lock_hard_drop = true + self.lock_drop = true + self.lock_hard_drop = true self.enable_hold = true self.next_queue_length = 3 self.coolregret_message = "COOL!!" self.coolregret_timer = 0 + + self.torikan_passed = false end function MarathonA3Game:getARE() - if self.speed_level < 700 then return 27 - elseif self.speed_level < 800 then return 18 - elseif self.speed_level < 1000 then return 14 - elseif self.speed_level < 1100 then return 8 - elseif self.speed_level < 1200 then return 7 - else return 6 end + if self.speed_level < 700 then return 27 + elseif self.speed_level < 800 then return 18 + elseif self.speed_level < 1000 then return 14 + elseif self.speed_level < 1100 then return 8 + elseif self.speed_level < 1200 then return 7 + else return 6 end end function MarathonA3Game:getLineARE() - if self.speed_level < 600 then return 27 - elseif self.speed_level < 700 then return 18 - elseif self.speed_level < 800 then return 14 - elseif self.speed_level < 1100 then return 8 - elseif self.speed_level < 1200 then return 7 - else return 6 end + if self.speed_level < 600 then return 27 + elseif self.speed_level < 700 then return 18 + elseif self.speed_level < 800 then return 14 + elseif self.speed_level < 1100 then return 8 + elseif self.speed_level < 1200 then return 7 + else return 6 end end function MarathonA3Game:getDasLimit() - if self.speed_level < 500 then return 15 - elseif self.speed_level < 900 then return 9 - else return 7 end + if self.speed_level < 500 then return 15 + elseif self.speed_level < 900 then return 9 + else return 7 end end function MarathonA3Game:getLineClearDelay() - if self.speed_level < 500 then return 40 - elseif self.speed_level < 600 then return 25 - elseif self.speed_level < 700 then return 16 - elseif self.speed_level < 800 then return 12 - elseif self.speed_level < 1100 then return 6 - elseif self.speed_level < 1200 then return 5 - else return 4 end + if self.speed_level < 500 then return 40 + elseif self.speed_level < 600 then return 25 + elseif self.speed_level < 700 then return 16 + elseif self.speed_level < 800 then return 12 + elseif self.speed_level < 1100 then return 6 + elseif self.speed_level < 1200 then return 5 + else return 4 end end function MarathonA3Game:getLockDelay() - if self.speed_level < 900 then return 30 - elseif self.speed_level < 1100 then return 17 - else return 15 end + if self.speed_level < 900 then return 30 + elseif self.speed_level < 1100 then return 17 + else return 15 end end function MarathonA3Game:getGravity() - if (self.speed_level < 30) then return 4/256 - elseif (self.speed_level < 35) then return 6/256 - elseif (self.speed_level < 40) then return 8/256 - elseif (self.speed_level < 50) then return 10/256 - elseif (self.speed_level < 60) then return 12/256 - elseif (self.speed_level < 70) then return 16/256 - elseif (self.speed_level < 80) then return 32/256 - elseif (self.speed_level < 90) then return 48/256 - elseif (self.speed_level < 100) then return 64/256 - elseif (self.speed_level < 120) then return 80/256 - elseif (self.speed_level < 140) then return 96/256 - elseif (self.speed_level < 160) then return 112/256 - elseif (self.speed_level < 170) then return 128/256 - elseif (self.speed_level < 200) then return 144/256 - elseif (self.speed_level < 220) then return 4/256 - elseif (self.speed_level < 230) then return 32/256 - elseif (self.speed_level < 233) then return 64/256 - elseif (self.speed_level < 236) then return 96/256 - elseif (self.speed_level < 239) then return 128/256 - elseif (self.speed_level < 243) then return 160/256 - elseif (self.speed_level < 247) then return 192/256 - elseif (self.speed_level < 251) then return 224/256 - elseif (self.speed_level < 300) then return 1 - elseif (self.speed_level < 330) then return 2 - elseif (self.speed_level < 360) then return 3 - elseif (self.speed_level < 400) then return 4 - elseif (self.speed_level < 420) then return 5 - elseif (self.speed_level < 450) then return 4 - elseif (self.speed_level < 500) then return 3 - else return 20 - end + if (self.speed_level < 30) then return 4/256 + elseif (self.speed_level < 35) then return 6/256 + elseif (self.speed_level < 40) then return 8/256 + elseif (self.speed_level < 50) then return 10/256 + elseif (self.speed_level < 60) then return 12/256 + elseif (self.speed_level < 70) then return 16/256 + elseif (self.speed_level < 80) then return 32/256 + elseif (self.speed_level < 90) then return 48/256 + elseif (self.speed_level < 100) then return 64/256 + elseif (self.speed_level < 120) then return 80/256 + elseif (self.speed_level < 140) then return 96/256 + elseif (self.speed_level < 160) then return 112/256 + elseif (self.speed_level < 170) then return 128/256 + elseif (self.speed_level < 200) then return 144/256 + elseif (self.speed_level < 220) then return 4/256 + elseif (self.speed_level < 230) then return 32/256 + elseif (self.speed_level < 233) then return 64/256 + elseif (self.speed_level < 236) then return 96/256 + elseif (self.speed_level < 239) then return 128/256 + elseif (self.speed_level < 243) then return 160/256 + elseif (self.speed_level < 247) then return 192/256 + elseif (self.speed_level < 251) then return 224/256 + elseif (self.speed_level < 300) then return 1 + elseif (self.speed_level < 330) then return 2 + elseif (self.speed_level < 360) then return 3 + elseif (self.speed_level < 400) then return 4 + elseif (self.speed_level < 420) then return 5 + elseif (self.speed_level < 450) then return 4 + elseif (self.speed_level < 500) then return 3 + else return 20 + end end function MarathonA3Game:advanceOneFrame() @@ -146,30 +149,31 @@ end function MarathonA3Game:onPieceEnter() if (self.level % 100 ~= 99) and self.level ~= 998 and self.frames ~= 0 then - self:updateSectionTimes(self.level, self.level + 1) - self.level = self.level + 1 - self.speed_level = self.speed_level + 1 - end + self:updateSectionTimes(self.level, self.level + 1) + self.level = self.level + 1 + self.speed_level = self.speed_level + 1 + self.torikan_passed = self.level >= 500 and true or false + end end local cleared_row_levels = {1, 2, 4, 6} function MarathonA3Game:onLineClear(cleared_row_count) - local advanced_levels = cleared_row_levels[cleared_row_count] - self:updateSectionTimes(self.level, self.level + advanced_levels) - if not self.clear then - self.level = math.min(self.level + advanced_levels, 999) - end - self.speed_level = self.speed_level + advanced_levels - if self.level == 999 and not self.clear then - self.clear = true - self.grid:clear() - self.roll_frames = -150 - end - if self.level >= 500 and self.frames >= 25200 then + local advanced_levels = cleared_row_levels[cleared_row_count] + self:updateSectionTimes(self.level, self.level + advanced_levels) + if not self.clear then + self.level = math.min(self.level + advanced_levels, 999) + end + self.speed_level = self.speed_level + advanced_levels + if self.level == 999 and not self.clear then + self.clear = true + self.grid:clear() + self.roll_frames = -150 + end + if not self.torikan_passed and self.level >= 500 and self.frames >= 25200 then self.level = 500 self.game_over = true - end + end end local cool_cutoffs = { @@ -192,48 +196,53 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level) table.insert(self.section_times, section_time) 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 self.section_cool_grade = self.section_cool_grade - 1 table.insert(self.section_status, "regret") self.coolregret_message = "REGRET!!" self.coolregret_timer = 300 - elseif section <= 9 and self.section_status[section - 1] == "cool" and - self.section_70_times[section] < self.section_70_times[section - 1] + 120 then + elseif self.section_cool 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 - 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 table.insert(self.section_status, "none") 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 section_70_time = self.frames - self.section_start_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 function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines) - self:updateGrade(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 + if not self.clear then + self:updateGrade(cleared_lines) + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end @@ -353,6 +362,8 @@ function MarathonA3Game:getLetterGrade() return "M" .. tostring(grade - 17) elseif grade < 32 then return master_grades[grade - 26] + elseif grade >= 32 and self.roll_frames < 3238 then + return "MM" else return "GM" end @@ -398,9 +409,9 @@ function MarathonA3Game:drawScoringInfo() love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left") local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end -- draw section time data current_section = math.floor(self.level / 100) + 1 @@ -427,21 +438,24 @@ function MarathonA3Game:drawScoringInfo() current_x = section_70_x 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 - love.graphics.printf(self.coolregret_message, 64, 400, 160, "center") - self.coolregret_timer = self.coolregret_timer - 1 - end + 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.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.setColor(1, 1, 1, 1) love.graphics.printf(self.level, 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end love.graphics.setFont(font_8x11) love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center") diff --git a/tetris/modes/marathon_ax4.lua b/tetris/modes/marathon_ax4.lua index 5495c41..dad39a9 100644 --- a/tetris/modes/marathon_ax4.lua +++ b/tetris/modes/marathon_ax4.lua @@ -24,12 +24,13 @@ function MarathonAX4Game:new() self.section_clear = false self.lock_drop = true + self.lock_hard_drop = true self.enable_hold = true self.next_queue_length = 3 end 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 < 60 then return 12 elseif self.lines < 70 then return 10 @@ -43,14 +44,14 @@ function MarathonAX4Game:getLineARE() end 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 < 70 then return 8 else return 7 end end function MarathonAX4Game:getLineClearDelay() - if self.lines < 10 then return 14 + if self.lines < 10 then return 14 elseif self.lines < 30 then return 9 else return 5 end end @@ -98,6 +99,7 @@ function MarathonAX4Game:onLineClear(cleared_row_count) self:updateSectionTimes(self.lines, new_lines) self.lines = math.min(new_lines, 150) if self.lines == 150 then + self.grid:clear() self.clear = true self.roll_frames = -150 end diff --git a/tetris/modes/marathon_c89.lua b/tetris/modes/marathon_c89.lua deleted file mode 100644 index bc4b75d..0000000 --- a/tetris/modes/marathon_c89.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/pacer_test.lua b/tetris/modes/pacer_test.lua deleted file mode 100644 index 0119b3d..0000000 --- a/tetris/modes/pacer_test.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/phantom_mania.lua b/tetris/modes/phantom_mania.lua index bab6c9d..3ae9f39 100644 --- a/tetris/modes/phantom_mania.lua +++ b/tetris/modes/phantom_mania.lua @@ -15,13 +15,14 @@ function PhantomManiaGame:new() PhantomManiaGame.super:new() self.lock_drop = true + self.lock_hard_drop = true self.next_queue_length = 1 - - self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } + + self.SGnames = { + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } self.roll_frames = 0 self.combo = 1 @@ -29,7 +30,7 @@ function PhantomManiaGame:new() end 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 < 400 then return 8 elseif self.level < 500 then return 7 @@ -37,14 +38,14 @@ function PhantomManiaGame:getARE() end 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 < 500 then return 7 else return 6 end end 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 < 400 then return 9 else return 7 end @@ -55,7 +56,7 @@ function PhantomManiaGame:getLineClearDelay() end 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 < 300 then return 22 elseif self.level < 400 then return 18 @@ -117,16 +118,17 @@ function PhantomManiaGame:onLineClear(cleared_row_count) end function PhantomManiaGame: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 + if not self.clear then + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 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 love.graphics.printf("SCORE", text_x, 200, 40, "left") love.graphics.printf("LEVEL", text_x, 320, 40, "left") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end love.graphics.setFont(font_3x5_3) if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end @@ -185,9 +187,9 @@ function PhantomManiaGame:drawScoringInfo() love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right") end - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end end function PhantomManiaGame:getSectionEndLevel() diff --git a/tetris/modes/phantom_mania2.lua b/tetris/modes/phantom_mania2.lua index 5dee33a..a4453fb 100644 --- a/tetris/modes/phantom_mania2.lua +++ b/tetris/modes/phantom_mania2.lua @@ -16,7 +16,6 @@ PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make function PhantomMania2Game:new() PhantomMania2Game.super:new() - self.level = 0 self.grade = 0 self.garbage = 0 self.clear = false @@ -26,27 +25,31 @@ function PhantomMania2Game:new() self.hold_age = 0 self.queue_age = 0 self.roll_points = 0 - - self.SGnames = { - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", - "GM" - } + + self.SGnames = { + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", + "GM" + } self.randomizer = History6RollsRandomizer() self.lock_drop = true + self.lock_hard_drop = true self.enable_hold = true self.next_queue_length = 3 + + self.coolregret_message = "" + self.coolregret_timer = 0 end function PhantomMania2Game:getARE() - if self.level < 300 then return 12 + if self.level < 300 then return 12 else return 6 end end 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 < 500 then return 6 elseif self.level < 1300 then return 5 @@ -54,7 +57,7 @@ function PhantomMania2Game:getLineARE() end function PhantomMania2Game:getDasLimit() - if self.level < 200 then return 9 + if self.level < 200 then return 9 elseif self.level < 500 then return 7 else return 5 end end @@ -64,7 +67,7 @@ function PhantomMania2Game:getLineClearDelay() end 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 < 500 then return 15 elseif self.level < 600 then return 13 @@ -122,6 +125,8 @@ function PhantomMania2Game:advanceOneFrame() return false elseif self.roll_frames > 3238 then 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 end elseif self.ready_frames == 0 then @@ -143,7 +148,8 @@ function PhantomMania2Game:onPieceEnter() end 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) if not self.clear then @@ -162,7 +168,8 @@ function PhantomMania2Game:onLineClear(cleared_row_count) end self:advanceBottomRow(-cleared_row_count) 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 self.roll_points = self.roll_points - 100 self.grade = self.grade + 1 @@ -171,24 +178,27 @@ function PhantomMania2Game:onLineClear(cleared_row_count) end function PhantomMania2Game:onPieceLock(piece, cleared_row_count) + self.super:onPieceLock() if cleared_row_count == 0 then self:advanceBottomRow(1) end end function PhantomMania2Game:onHold() + self.super.onHold() self.hold_age = 0 end function PhantomMania2Game: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 + if not self.clear then + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end @@ -213,8 +223,13 @@ function PhantomMania2Game:updateSectionTimes(old_level, new_level) self.section_start_time = self.frames if section_time <= cool_cutoffs[section] then self.grade = self.grade + 2 + self.coolregret_message = "COOL!!" + self.coolregret_timer = 300 elseif section_time <= regret_cutoffs[section] then self.grade = self.grade + 1 + else + self.coolregret_message = "REGRET!!" + self.coolregret_timer = 300 end end end @@ -291,10 +306,17 @@ function PhantomMania2Game:drawScoringInfo() 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") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + 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.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left") @@ -305,10 +327,10 @@ function PhantomMania2Game:drawScoringInfo() else love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right") end - - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end end function PhantomMania2Game:getBackground() diff --git a/tetris/modes/phantom_mania_n.lua b/tetris/modes/phantom_mania_n.lua deleted file mode 100644 index 708ae7b..0000000 --- a/tetris/modes/phantom_mania_n.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/race_40.lua b/tetris/modes/race_40.lua deleted file mode 100644 index 6f03370..0000000 --- a/tetris/modes/race_40.lua +++ /dev/null @@ -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 diff --git a/tetris/modes/strategy.lua b/tetris/modes/strategy.lua index 70578b7..049a6e3 100644 --- a/tetris/modes/strategy.lua +++ b/tetris/modes/strategy.lua @@ -16,7 +16,6 @@ StrategyGame.tagline = "You have lots of time to think! Can you use it to place function StrategyGame:new() StrategyGame.super:new() - self.level = 0 self.clear = false self.completed = false self.roll_frames = 0 @@ -28,7 +27,7 @@ function StrategyGame:new() end 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 < 300 then return 48 elseif self.level < 400 then return 42 @@ -53,7 +52,7 @@ function StrategyGame:getLineClearDelay() end function StrategyGame:getLockDelay() - if self.level < 500 then return 8 + if self.level < 500 then return 8 elseif self.level < 700 then return 6 else return 4 end end @@ -84,7 +83,7 @@ function StrategyGame:advanceOneFrame() end 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 end end @@ -99,16 +98,17 @@ function StrategyGame:onLineClear(cleared_row_count) end function StrategyGame: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 + if not self.clear then + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end @@ -135,11 +135,11 @@ function StrategyGame:drawScoringInfo() love.graphics.setFont(font_3x5_3) 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 - love.graphics.printf(self.level, text_x, 370, 50, "right") + love.graphics.printf(self.level, text_x, 370, 40, "right") 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 diff --git a/tetris/modes/survival_2020.lua b/tetris/modes/survival_2020.lua index d15860a..7d385f7 100644 --- a/tetris/modes/survival_2020.lua +++ b/tetris/modes/survival_2020.lua @@ -27,6 +27,7 @@ function Survival2020Game:new() self.randomizer = History6RollsRandomizer() self.lock_drop = true + self.lock_hard_drop = true self.enable_hold = true self.next_queue_length = 3 end @@ -45,7 +46,7 @@ function Survival2020Game:getLineARE() end 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 < 1000 then return 5 elseif self.level < 1500 then return 4 @@ -174,16 +175,17 @@ function Survival2020Game:onLineClear(cleared_row_count) end function Survival2020Game: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 + if not self.clear then + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end diff --git a/tetris/modes/survival_a1.lua b/tetris/modes/survival_a1.lua index 4b10d4b..c635dd0 100644 --- a/tetris/modes/survival_a1.lua +++ b/tetris/modes/survival_a1.lua @@ -15,28 +15,28 @@ SurvivalA1Game.tagline = "The game starts fast and only gets faster!" function SurvivalA1Game:new() - SurvivalA1Game.super:new() - + SurvivalA1Game.super:new() + self.roll_frames = 0 - self.combo = 1 + self.combo = 1 self.bravos = 0 self.gm_conditions = { level300 = false, level500 = false, level999 = false - } - - self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } - + } + + self.SGnames = { + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } + self.randomizer = History4RollsRandomizer() - self.lock_drop = false - self.enable_hard_drop = false + self.lock_drop = false + self.enable_hard_drop = false self.enable_hold = false self.next_queue_length = 1 end @@ -62,29 +62,29 @@ function SurvivalA1Game:getLockDelay() end function SurvivalA1Game:getGravity() - return 20 + return 20 end local function getRankForScore(score) - if score < 400 then return {rank = "9", next = 400} - elseif score < 800 then return {rank = "8", next = 800} - elseif score < 1400 then return {rank = "7", next = 1400} - elseif score < 2000 then return {rank = "6", next = 2000} - elseif score < 3500 then return {rank = "5", next = 3500} - elseif score < 5500 then return {rank = "4", next = 5500} - elseif score < 8000 then return {rank = "3", next = 8000} - elseif score < 12000 then return {rank = "2", next = 12000} - elseif score < 16000 then return {rank = "1", next = 16000} - elseif score < 22000 then return {rank = "S1", next = 22000} - elseif score < 30000 then return {rank = "S2", next = 30000} - elseif score < 40000 then return {rank = "S3", next = 40000} - elseif score < 52000 then return {rank = "S4", next = 52000} - elseif score < 66000 then return {rank = "S5", next = 66000} - elseif score < 82000 then return {rank = "S6", next = 82000} - elseif score < 100000 then return {rank = "S7", next = 100000} - elseif score < 120000 then return {rank = "S8", next = 120000} - else return {rank = "S9", next = "???"} - end + if score < 400 then return {rank = "9", next = 400} + elseif score < 800 then return {rank = "8", next = 800} + elseif score < 1400 then return {rank = "7", next = 1400} + elseif score < 2000 then return {rank = "6", next = 2000} + elseif score < 3500 then return {rank = "5", next = 3500} + elseif score < 5500 then return {rank = "4", next = 5500} + elseif score < 8000 then return {rank = "3", next = 8000} + elseif score < 12000 then return {rank = "2", next = 12000} + elseif score < 16000 then return {rank = "1", next = 16000} + elseif score < 22000 then return {rank = "S1", next = 22000} + elseif score < 30000 then return {rank = "S2", next = 30000} + elseif score < 40000 then return {rank = "S3", next = 40000} + elseif score < 52000 then return {rank = "S4", next = 52000} + elseif score < 66000 then return {rank = "S5", next = 66000} + elseif score < 82000 then return {rank = "S6", next = 82000} + elseif score < 100000 then return {rank = "S7", next = 100000} + elseif score < 120000 then return {rank = "S8", next = 120000} + else return {rank = "S9", next = "???"} + end end function SurvivalA1Game:advanceOneFrame() @@ -106,47 +106,48 @@ function SurvivalA1Game:onPieceEnter() end 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 local new_level = math.min(self.level + cleared_row_count, 999) - if self.level == 999 then + if new_level == 999 then self.clear = true else - self.level = new_level + self.level = new_level end end end function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines) - if self.grid:checkForBravo(cleared_lines) then - self.bravo = 4 - self.bravos = self.bravos + 1 - else self.bravo = 1 end - if cleared_lines > 0 then - self.score = self.score + ( - (math.ceil((level + cleared_lines) / 4) + drop_bonus) * - cleared_lines * self.bravo * self.combo - ) - self.lines = self.lines + cleared_lines - self.combo = self.combo + (cleared_lines - 1) * 2 - else + if not self.clear then + if self.grid:checkForBravo(cleared_lines) then + self.bravo = 4 + self.bravos = self.bravos + 1 + else self.bravo = 1 end + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo * self.bravo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end function SurvivalA1Game:checkGMRequirements(old_level, new_level) if old_level < 300 and new_level >= 300 then - if self.score > 12000 and self.frames <= frameTime(4,15) then + if self.score >= 12000 and self.frames <= frameTime(4,15) then self.gm_conditions["level300"] = true end elseif old_level < 500 and new_level >= 500 then - if self.score > 40000 and self.frames <= frameTime(7,30) then + if self.score >= 40000 and self.frames <= frameTime(7,30) then self.gm_conditions["level500"] = true end elseif old_level < 999 and new_level >= 999 then - if self.score > 126000 and self.frames <= frameTime(13,30) then - self.gm_conditions["level900"] = true + if self.score >= 126000 and self.frames <= frameTime(13,30) then + self.gm_conditions["level999"] = true end end end @@ -170,16 +171,16 @@ function SurvivalA1Game:drawScoringInfo() love.graphics.printf("SCORE", 240, 200, 40, "left") love.graphics.printf("NEXT RANK", 240, 260, 90, "left") love.graphics.printf("LEVEL", 240, 320, 40, "left") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end love.graphics.setFont(font_3x5_3) love.graphics.printf(self.score, 240, 220, 90, "left") - if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["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") else 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(self.level, 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end love.graphics.setFont(font_8x11) diff --git a/tetris/modes/survival_a2.lua b/tetris/modes/survival_a2.lua index c2995fe..aad38e2 100644 --- a/tetris/modes/survival_a2.lua +++ b/tetris/modes/survival_a2.lua @@ -19,14 +19,15 @@ function SurvivalA2Game:new() self.roll_frames = 0 self.combo = 1 self.randomizer = History6RollsRandomizer() - - self.SGnames = { - "9", "8", "7", "6", "5", "4", "3", "2", "1", - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "GM" - } + + self.SGnames = { + "9", "8", "7", "6", "5", "4", "3", "2", "1", + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "GM" + } self.lock_drop = true + self.lock_hard_drop = true end function SurvivalA2Game:getARE() @@ -88,7 +89,7 @@ function SurvivalA2Game:advanceOneFrame() end 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 end end @@ -98,9 +99,9 @@ function SurvivalA2Game:onLineClear(cleared_row_count) local new_level = math.min(self.level + cleared_row_count, 999) if self.level == 999 or self:hitTorikan(self.level, new_level) then self.clear = true - if self.level < 999 then - self.game_over = true - end + if self.level < 999 then + self.game_over = true + end else self.level = new_level end @@ -108,17 +109,18 @@ function SurvivalA2Game:onLineClear(cleared_row_count) end function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines) - if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end - if cleared_lines > 0 then - self.score = self.score + ( - (math.ceil((level + cleared_lines) / 4) + drop_bonus) * - cleared_lines * self.bravo * self.combo - ) - self.lines = self.lines + cleared_lines - self.combo = self.combo + (cleared_lines - 1) * 2 - else + if not self.clear then + if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo * self.bravo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end @@ -149,19 +151,21 @@ function SurvivalA2Game:drawScoringInfo() 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("LEVEL", text_x, 320, 40, "left") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end love.graphics.setFont(font_3x5_3) love.graphics.printf(self.score, text_x, 220, 90, "left") + if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1) + 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 love.graphics.printf(self.level, text_x, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right") - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end end function SurvivalA2Game:getSectionEndLevel() diff --git a/tetris/modes/survival_a3.lua b/tetris/modes/survival_a3.lua index 6233782..2038266 100644 --- a/tetris/modes/survival_a3.lua +++ b/tetris/modes/survival_a3.lua @@ -12,11 +12,8 @@ SurvivalA3Game.hash = "SurvivalA3" SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to level 1300?" - - function SurvivalA3Game:new() SurvivalA3Game.super:new() - self.level = 0 self.grade = 0 self.garbage = 0 self.clear = false @@ -24,14 +21,15 @@ function SurvivalA3Game:new() self.roll_frames = 0 self.combo = 1 self.randomizer = History6RollsRandomizer() - - self.SGnames = { - "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", - "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", - "GM" - } - + + self.SGnames = { + "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", + "GM" + } + self.lock_drop = true + self.lock_hard_drop = true self.enable_hold = true self.next_queue_length = 3 @@ -41,19 +39,19 @@ end function SurvivalA3Game:initialize(ruleset) - self.torikan_time = frameTime(2,28) - if ruleset.world then self.torikan_time = frameTime(3,03) end - self.super.initialize(self, ruleset) - -- ^ notice the . here instead of the : + self.torikan_time = frameTime(2,28) + if ruleset.world then self.torikan_time = frameTime(3,03) end + self.super.initialize(self, ruleset) + -- ^ notice the . here instead of the : end function SurvivalA3Game:getARE() - if self.level < 300 then return 12 + if self.level < 300 then return 12 else return 6 end end 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 < 500 then return 6 elseif self.level < 1300 then return 5 @@ -61,7 +59,7 @@ function SurvivalA3Game:getLineARE() end function SurvivalA3Game:getDasLimit() - if self.level < 100 then return 9 + if self.level < 100 then return 9 elseif self.level < 500 then return 7 else return 5 end end @@ -72,7 +70,7 @@ function SurvivalA3Game:getLineClearDelay() end 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 < 500 then return 15 elseif self.level < 600 then return 13 @@ -140,21 +138,20 @@ function SurvivalA3Game:onPieceEnter() end 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) if not self.clear then local new_level = self.level + cleared_row_levels[cleared_row_count] self:updateSectionTimes(self.level, new_level) if new_level >= 1300 or self:hitTorikan(self.level, new_level) then - self.clear = true + self.clear = true if new_level >= 1300 then self.level = 1300 - self.grid:clear() - self.big_mode = true - self.roll_frames = -150 - else - self.game_over = true + self.grid:clear() + self.big_mode = true + self.roll_frames = -150 + else + self.game_over = true end else self.level = math.min(new_level, 1300) @@ -164,20 +161,22 @@ function SurvivalA3Game:onLineClear(cleared_row_count) end function SurvivalA3Game:onPieceLock(piece, cleared_row_count) + self.super:onPieceLock() if cleared_row_count == 0 then self:advanceBottomRow(1) end end function SurvivalA3Game: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 + if not self.clear then + if cleared_lines > 0 then + self.combo = self.combo + (cleared_lines - 1) * 2 + self.score = self.score + ( + (math.ceil((level + cleared_lines) / 4) + drop_bonus) * + cleared_lines * self.combo + ) + else + self.combo = 1 + end self.drop_bonus = 0 - self.combo = 1 end end @@ -229,10 +228,10 @@ function SurvivalA3Game:drawScoringInfo() 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") - local sg = self.grid:checkSecretGrade() - if sg >= 5 then - love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") - end + local sg = self.grid:checkSecretGrade() + if sg >= 5 then + love.graphics.printf("SECRET GRADE", 240, 430, 180, "left") + end if(self.coolregret_timer > 0) then love.graphics.printf(self.coolregret_message, 64, 400, 160, "center") @@ -243,7 +242,10 @@ function SurvivalA3Game:drawScoringInfo() self:drawSectionTimesWithSplits(current_section) 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.setColor(1, 1, 1, 1) love.graphics.printf(self.score, text_x, 220, 90, "left") love.graphics.printf(self.level, text_x, 340, 50, "right") if self.clear then @@ -251,9 +253,9 @@ function SurvivalA3Game:drawScoringInfo() else love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right") end - if sg >= 5 then - love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") - end + if sg >= 5 then + love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left") + end end function SurvivalA3Game:getBackground() diff --git a/tetris/randomizers/bag5.lua b/tetris/randomizers/bag5.lua deleted file mode 100644 index 7344bcb..0000000 --- a/tetris/randomizers/bag5.lua +++ /dev/null @@ -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 diff --git a/tetris/randomizers/bag5alt.lua b/tetris/randomizers/bag5alt.lua deleted file mode 100755 index 90cd16e..0000000 --- a/tetris/randomizers/bag5alt.lua +++ /dev/null @@ -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 diff --git a/tetris/randomizers/bag7noSZOstart.lua b/tetris/randomizers/bag7noSZOstart.lua index 30ec8f3..266a371 100644 --- a/tetris/randomizers/bag7noSZOstart.lua +++ b/tetris/randomizers/bag7noSZOstart.lua @@ -3,27 +3,27 @@ local Randomizer = require 'tetris.randomizers.randomizer' local Bag7NoSZOStartRandomizer = Randomizer:extend() function Bag7NoSZOStartRandomizer:shuffleBag() - local b = self.bag - local ln = #b - for i = 1, ln do - local j = math.random(i, ln) - b[i], b[j] = b[j], b[i] - end + local b = self.bag + local ln = #b + for i = 1, ln do + local j = math.random(i, ln) + b[i], b[j] = b[j], b[i] + end end local function isnotSZO(x) return not(x == "S" or x == "Z" or x == "O") end function Bag7NoSZOStartRandomizer:initialize() self.bag = {"I", "J", "L", "O", "S", "T", "Z"} - repeat - self:shuffleBag() - until isnotSZO(self.bag[7]) + repeat + self:shuffleBag() + until isnotSZO(self.bag[7]) end function Bag7NoSZOStartRandomizer:generatePiece() if #self.bag == 0 then self.bag = {"I", "J", "L", "O", "S", "T", "Z"} - self:shuffleBag() + self:shuffleBag() end return table.remove(self.bag) end diff --git a/tetris/randomizers/bag8.lua b/tetris/randomizers/bag8.lua deleted file mode 100644 index 7c91791..0000000 --- a/tetris/randomizers/bag8.lua +++ /dev/null @@ -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 diff --git a/tetris/randomizers/bag_konoha.lua b/tetris/randomizers/bag_konoha.lua deleted file mode 100644 index cb9d78f..0000000 --- a/tetris/randomizers/bag_konoha.lua +++ /dev/null @@ -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 diff --git a/tetris/randomizers/history_4rolls.lua b/tetris/randomizers/history_4rolls.lua index c1e8f33..e598358 100644 --- a/tetris/randomizers/history_4rolls.lua +++ b/tetris/randomizers/history_4rolls.lua @@ -4,22 +4,22 @@ local History4RollsRandomizer = Randomizer:extend() function History4RollsRandomizer:initialize() self.history = {"Z", "Z", "Z", "Z"} - self.first = true + self.first = true end function History4RollsRandomizer:generatePiece() - if self.first then - self.first = false - return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) - else - local shapes = {"I", "J", "L", "O", "S", "T", "Z"} - for i = 1, 4 do - local x = math.random(7) - if not inHistory(shapes[x], self.history) or i == 4 then - return self:updateHistory(shapes[x]) - end - end - end + if self.first then + self.first = false + return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) + else + local shapes = {"I", "J", "L", "O", "S", "T", "Z"} + for i = 1, 4 do + local x = math.random(7) + if not inHistory(shapes[x], self.history) or i == 4 then + return self:updateHistory(shapes[x]) + end + end + end end function History4RollsRandomizer:updateHistory(shape) diff --git a/tetris/randomizers/history_6rolls.lua b/tetris/randomizers/history_6rolls.lua index f48543c..44c2d41 100644 --- a/tetris/randomizers/history_6rolls.lua +++ b/tetris/randomizers/history_6rolls.lua @@ -4,22 +4,22 @@ local History6RollsRandomizer = Randomizer:extend() function History6RollsRandomizer:initialize() self.history = {"Z", "S", "Z", "S"} - self.first = true + self.first = true end function History6RollsRandomizer:generatePiece() - if self.first then - self.first = false - return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) - else - local shapes = {"I", "J", "L", "O", "S", "T", "Z"} - for i = 1, 6 do - local x = math.random(7) - if not inHistory(shapes[x], self.history) or i == 6 then - return self:updateHistory(shapes[x]) - end - end - end + if self.first then + self.first = false + return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)]) + else + local shapes = {"I", "J", "L", "O", "S", "T", "Z"} + for i = 1, 6 do + local x = math.random(7) + if not inHistory(shapes[x], self.history) or i == 6 then + return self:updateHistory(shapes[x]) + end + end + end end function History6RollsRandomizer:updateHistory(shape) diff --git a/tetris/randomizers/history_6rolls_35bag.lua b/tetris/randomizers/history_6rolls_35bag.lua index 190d396..000332a 100644 --- a/tetris/randomizers/history_6rolls_35bag.lua +++ b/tetris/randomizers/history_6rolls_35bag.lua @@ -3,67 +3,67 @@ local Randomizer = require 'tetris.randomizers.randomizer' local History6Rolls35PoolRandomizer = Randomizer:extend() function History6Rolls35PoolRandomizer:initialize() - self.first = true + self.first = true self.history = {"Z", "S", "Z", "S"} self.pool = { "I", "I", "I", "I", "I", - "T", "T", "T", "T", "T", - "L", "L", "L", "L", "L", - "J", "J", "J", "J", "J", - "S", "S", "S", "S", "S", - "Z", "Z", "Z", "Z", "Z", - "O", "O", "O", "O", "O", + "T", "T", "T", "T", "T", + "L", "L", "L", "L", "L", + "J", "J", "J", "J", "J", + "S", "S", "S", "S", "S", + "Z", "Z", "Z", "Z", "Z", + "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 function History6Rolls35PoolRandomizer:generatePiece() - local index, x - if self.first then - local prevent = {"S", "Z", "O"} - repeat - index = math.random(#self.pool) - x = self.pool[index] - until not inHistory(x, prevent) - self.first = false - else - for i = 1, 6 do - index = math.random(#self.pool) - x = self.pool[index] - if not inHistory(x, self.history) or i == 6 then - break - end - end - end - self.pool[index] = self:updateHistory(x) - return x + local index, x + if self.first then + local prevent = {"S", "Z", "O"} + repeat + index = math.random(#self.pool) + x = self.pool[index] + until not inHistory(x, prevent) + self.first = false + else + for i = 1, 6 do + index = math.random(#self.pool) + x = self.pool[index] + if not inHistory(x, self.history) or i == 6 then + break + end + end + end + self.pool[index] = self:updateHistory(x) + return x end function History6Rolls35PoolRandomizer:updateHistory(shape) table.remove(self.history, 1) table.insert(self.history, shape) - local highdrought - local highdroughtcount = 0 - for k, v in pairs(self.droughts) do - if k == shape then - self.droughts[k] = 0 - else - self.droughts[k] = v + 1 - if v >= highdroughtcount then - highdrought = k - highdroughtcount = v - end - end - end + local highdrought + local highdroughtcount = 0 + for k, v in pairs(self.droughts) do + if k == shape then + self.droughts[k] = 0 + else + self.droughts[k] = v + 1 + if v >= highdroughtcount then + highdrought = k + highdroughtcount = v + end + end + end return highdrought end diff --git a/tetris/randomizers/recursive_bag.lua b/tetris/randomizers/recursive_bag.lua deleted file mode 100644 index 4f496f4..0000000 --- a/tetris/randomizers/recursive_bag.lua +++ /dev/null @@ -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 diff --git a/tetris/randomizers/sega.lua b/tetris/randomizers/sega.lua deleted file mode 100644 index 48c5e1c..0000000 --- a/tetris/randomizers/sega.lua +++ /dev/null @@ -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 diff --git a/tetris/rulesets/arika.lua b/tetris/rulesets/arika.lua index 89fa6d8..1ea883e 100644 --- a/tetris/rulesets/arika.lua +++ b/tetris/rulesets/arika.lua @@ -82,18 +82,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) ) and ( piece.rotation == 0 or piece.rotation == 2 ) then - 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) - for index, offset in pairs(offsets) do - if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then - if offset.x == 0 then - return - else - break - end - end - end - end + 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) + for index, offset in pairs(offsets) do + if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then + if offset.x == 0 then + return + else + break + end + end + end + end -- kick right, kick left 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 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 return ARS diff --git a/tetris/rulesets/arika_ace.lua b/tetris/rulesets/arika_ace.lua index 76f095d..141c0bd 100755 --- a/tetris/rulesets/arika_ace.lua +++ b/tetris/rulesets/arika_ace.lua @@ -7,13 +7,13 @@ ARS.name = "ACE-ARS" ARS.hash = "ArikaACE" ARS.colourscheme = { - I = "C", - L = "O", - J = "B", - S = "G", - Z = "R", - O = "Y", - T = "M", + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", } ARS.softdrop_lock = false @@ -98,18 +98,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) ) and ( piece.rotation == 0 or piece.rotation == 2 ) then - 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) - for index, offset in pairs(offsets) do - if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then - if offset.x == 0 then - return - else - break - end - end - end - end + 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) + for index, offset in pairs(offsets) do + if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then + if offset.x == 0 then + return + else + break + end + end + end + end if piece.shape == "I" then -- special kick rules for I @@ -138,20 +138,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) end end else - -- 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}) - elseif piece.shape == "T" - and new_piece.rotation == 0 - and piece.floorkick == 0 - and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) - then - -- T floorkick - piece.floorkick = piece.floorkick + 1 - piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) - end + -- 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}) + elseif piece.shape == "T" + and new_piece.rotation == 0 + and piece.floorkick == 0 + and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) + then + -- T floorkick + piece.floorkick = piece.floorkick + 1 + piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) + end end end @@ -185,7 +185,14 @@ function ARS:onPieceRotate(piece, grid) 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 return ARS diff --git a/tetris/rulesets/arika_ace2.lua b/tetris/rulesets/arika_ace2.lua index 2d90b48..18a445f 100644 --- a/tetris/rulesets/arika_ace2.lua +++ b/tetris/rulesets/arika_ace2.lua @@ -85,18 +85,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) ) and ( piece.rotation == 0 or piece.rotation == 2 ) then - 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) - for index, offset in pairs(offsets) do - if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then - if offset.x == 0 then - return - else - break - end - end - end - end + 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) + for index, offset in pairs(offsets) do + if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then + if offset.x == 0 then + return + else + break + end + end + end + end if piece.shape == "I" then -- special kick rules for I @@ -125,20 +125,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) end end else - -- 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}) - elseif piece.shape == "T" - and new_piece.rotation == 0 - and piece.floorkick == 0 - and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) - then - -- T floorkick - piece.floorkick = piece.floorkick + 1 - piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) - end + -- 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}) + elseif piece.shape == "T" + and new_piece.rotation == 0 + and piece.floorkick == 0 + and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) + then + -- T floorkick + piece.floorkick = piece.floorkick + 1 + piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) + end end end @@ -172,7 +172,14 @@ function ARS:onPieceRotate(piece, grid) 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 return ARS diff --git a/tetris/rulesets/arika_srs.lua b/tetris/rulesets/arika_srs.lua index e92660b..fc52295 100755 --- a/tetris/rulesets/arika_srs.lua +++ b/tetris/rulesets/arika_srs.lua @@ -7,19 +7,17 @@ SRS.name = "ACE-SRS" SRS.hash = "ACE Standard" SRS.world = true SRS.colourscheme = { - I = "C", - L = "O", - J = "B", - S = "G", - Z = "R", - O = "Y", - T = "M", + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", } SRS.softdrop_lock = false SRS.harddrop_lock = true -SRS.enable_IRS_wallkicks = true - SRS.spawn_positions = { I = { x=5, y=2 }, J = { x=4, y=3 }, @@ -186,5 +184,12 @@ function SRS:onPieceRotate(piece, grid) 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 diff --git a/tetris/rulesets/arika_ti.lua b/tetris/rulesets/arika_ti.lua index eec3320..7ed012a 100644 --- a/tetris/rulesets/arika_ti.lua +++ b/tetris/rulesets/arika_ti.lua @@ -85,18 +85,18 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) ) and ( piece.rotation == 0 or piece.rotation == 2 ) then - 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) - for index, offset in pairs(offsets) do - if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then - if offset.x == 0 then - return - else - break - end - end - end - end + 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) + for index, offset in pairs(offsets) do + if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then + if offset.x == 0 then + return + else + break + end + end + end + end if piece.shape == "I" then -- special kick rules for I @@ -125,20 +125,20 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid) end end else - -- 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}) - elseif piece.shape == "T" - and new_piece.rotation == 0 - and piece.floorkick == 0 - and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) - then - -- T floorkick - piece.floorkick = piece.floorkick + 1 - piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) - end + -- 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}) + elseif piece.shape == "T" + and new_piece.rotation == 0 + and piece.floorkick == 0 + and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) + then + -- T floorkick + piece.floorkick = piece.floorkick + 1 + piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1}) + end end end @@ -151,7 +151,14 @@ function ARS:onPieceDrop(piece, grid) piece.lock_delay = 0 -- step reset 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 return ARS diff --git a/tetris/rulesets/cambridge.lua b/tetris/rulesets/cambridge.lua index 2b0d963..4aaf96a 100644 --- a/tetris/rulesets/cambridge.lua +++ b/tetris/rulesets/cambridge.lua @@ -363,10 +363,10 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial) end if rot_dir == 0 then return end - - if self.world and config.gamesettings.world_reverse == 2 then - rot_dir = 4 - rot_dir - end + + if self.world and config.gamesettings.world_reverse == 2 then + rot_dir = 4 - rot_dir + end local new_piece = piece:withRelativeRotation(rot_dir) self:attemptWallkicks(piece, new_piece, rot_dir, grid) diff --git a/tetris/rulesets/ruleset.lua b/tetris/rulesets/ruleset.lua index 0833116..89042d2 100644 --- a/tetris/rulesets/ruleset.lua +++ b/tetris/rulesets/ruleset.lua @@ -9,13 +9,13 @@ Ruleset.hash = "" -- Arika-type ruleset defaults Ruleset.world = false Ruleset.colourscheme = { - I = "R", - L = "O", - J = "B", - S = "M", - Z = "G", - O = "Y", - T = "C", + I = "R", + L = "O", + J = "B", + S = "M", + Z = "G", + O = "Y", + T = "C", } Ruleset.softdrop_lock = true Ruleset.harddrop_lock = false @@ -24,6 +24,60 @@ Ruleset.enable_IRS_wallkicks = false -- 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) local new_inputs = {} @@ -53,7 +107,7 @@ function Ruleset:attemptRotate(new_inputs, piece, grid, initial) 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 end @@ -134,8 +188,8 @@ function Ruleset:initializePiece( else spawn_positions = self.spawn_positions 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, { x = spawn_positions[data.shape].x, y = spawn_positions[data.shape].y diff --git a/tetris/rulesets/standard_exp.lua b/tetris/rulesets/standard_exp.lua index 2c8b260..601f614 100755 --- a/tetris/rulesets/standard_exp.lua +++ b/tetris/rulesets/standard_exp.lua @@ -7,13 +7,13 @@ SRS.name = "Guideline SRS" SRS.hash = "Standard" SRS.world = true SRS.colourscheme = { - I = "C", - L = "O", - J = "B", - S = "G", - Z = "R", - O = "Y", - T = "M", + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", } SRS.softdrop_lock = false SRS.harddrop_lock = true @@ -132,13 +132,13 @@ SRS.wallkicks_line = { }; function SRS:check_new_low(piece) - for _, block in pairs(piece:getBlockOffsets()) do - local y = piece.position.y + block.y - if y > piece.lowest_y then - piece.manipulations = 0 - piece.lowest_y = y - end - end + for _, block in pairs(piece:getBlockOffsets()) do + local y = piece.position.y + block.y + if y > piece.lowest_y then + piece.manipulations = 0 + piece.lowest_y = y + end + end end -- Component functions. @@ -170,16 +170,16 @@ end function SRS:onPieceCreate(piece, grid) piece.manipulations = 0 - piece.lowest_y = -math.huge + piece.lowest_y = -math.huge end function SRS:onPieceDrop(piece, grid) - self:check_new_low(piece) - if piece.manipulations >= 15 and piece:isDropBlocked(grid) then - piece.locked = true - else - piece.lock_delay = 0 -- step reset - end + self:check_new_low(piece) + if piece.manipulations >= 15 and piece:isDropBlocked(grid) then + piece.locked = true + else + piece.lock_delay = 0 -- step reset + end end function SRS:onPieceMove(piece, grid) @@ -194,8 +194,8 @@ end function SRS:onPieceRotate(piece, grid) piece.lock_delay = 0 -- rotate reset - self:check_new_low(piece) - piece.manipulations = piece.manipulations + 1 + self:check_new_low(piece) + piece.manipulations = piece.manipulations + 1 if piece:isDropBlocked(grid) then if piece.manipulations >= 15 then piece.locked = true diff --git a/tetris/rulesets/ti_srs.lua b/tetris/rulesets/ti_srs.lua index 9c4cc1f..6f79f34 100644 --- a/tetris/rulesets/ti_srs.lua +++ b/tetris/rulesets/ti_srs.lua @@ -7,19 +7,17 @@ SRS.name = "Ti-World" SRS.hash = "Bad I-kicks" SRS.world = true SRS.colourscheme = { - I = "C", - L = "O", - J = "B", - S = "G", - Z = "R", - O = "Y", - T = "M", + I = "C", + L = "O", + J = "B", + S = "G", + Z = "R", + O = "Y", + T = "M", } SRS.softdrop_lock = false SRS.harddrop_lock = true -SRS.enable_IRS_wallkicks = true - SRS.spawn_positions = { I = { x=5, y=4 }, J = { x=4, y=5 }, @@ -187,5 +185,12 @@ function SRS:onPieceRotate(piece, grid) 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 diff --git a/tetris/rulesets/unrefactored_rulesets/bonkers.lua b/tetris/rulesets/unrefactored_rulesets/bonkers.lua deleted file mode 100644 index b29b915..0000000 --- a/tetris/rulesets/unrefactored_rulesets/bonkers.lua +++ /dev/null @@ -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 diff --git a/tetris/rulesets/unrefactored_rulesets/shirase.lua b/tetris/rulesets/unrefactored_rulesets/shirase.lua deleted file mode 100644 index d1ad015..0000000 --- a/tetris/rulesets/unrefactored_rulesets/shirase.lua +++ /dev/null @@ -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 diff --git a/tetris/rulesets/unrefactored_rulesets/super302.lua b/tetris/rulesets/unrefactored_rulesets/super302.lua deleted file mode 100644 index 8b45a55..0000000 --- a/tetris/rulesets/unrefactored_rulesets/super302.lua +++ /dev/null @@ -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 diff --git a/tetris/rulesets/unrefactored_rulesets/tengen.lua b/tetris/rulesets/unrefactored_rulesets/tengen.lua deleted file mode 100644 index 9b6ccfd..0000000 --- a/tetris/rulesets/unrefactored_rulesets/tengen.lua +++ /dev/null @@ -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