diff --git a/README.md b/README.md index 4305da6..6c33cdb 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Cambridge Welcome to Cambridge, the next open-source falling-block game engine! -The project is written and maintained exclusively by [Milla](https://github.com/MillaBasset), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)! +The project is written and maintained exclusively by [Milla](https://github.com/MillaBasset), [joezeng](https://github.com/joezeng) and [Oshisaure](https://github.com/oshisaure)! The Discord server has been reopened! https://discord.gg/AADZUmgsph @@ -16,11 +16,11 @@ Playing the game ### Windows -You do not need LÖVE on Windows, as it comes bundled with the program. +You do not need LÖVE on Windows, as it comes bundled with the program. #### Stable release -To get the stable release, simply download either `cambridge-win32.zip` (32-bit) or `cambridge-windows.zip` (64-bit) in the [latest release](https://github.com/MillaBasset/cambridge/releases/latest). +To get the stable release, simply download either `cambridge-win32.zip` (32-bit) or `cambridge-windows.zip` (64-bit) in the [latest release](https://github.com/MillaBasset/cambridge/releases/latest). All assets needed are bundled with the executable. @@ -28,11 +28,11 @@ All assets needed are bundled with the executable. If you want the bleeding edge version, download [this](https://github.com/MillaBasset/cambridge/archive/master.zip). Extract the ZIP to a folder of your choosing. -Assuming you're on a 64-bit system, you can double-click `start_win64.bat` to run the game. If that doesn't work, open a Command Prompt where you extracted Cambridge and run: +If you're on Windows, you can double-click `start.bat` to run the game. If that doesn't work, open a Command Prompt where you extracted Cambridge and run: dist\windows\love.exe . -If you're on a 32-bit system, you'll want to double-click `start_win32.bat`. If that doesn't work, run this instead: +If that doesn't work, run this instead, still using Command Prompt where you extracted Cambridge: dist\win32\love.exe . @@ -107,4 +107,4 @@ Other Notable Games - [Puzzle Trial](https://kagamine-rin.itch.io/puzzle-trial) by Rin - [stackfuse](https://github.com/sinefuse/stackfuse) by sinefuse -![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png) +![Cambridge Logo](https://cdn.discordapp.com/attachments/827186653772644452/1077674343544393820/Icon_2.png) diff --git a/funcs.lua b/funcs.lua index dfd8aa3..831e97f 100644 --- a/funcs.lua +++ b/funcs.lua @@ -60,6 +60,9 @@ function formatTime(frames) min = math.floor(frames/3600) sec = math.floor(frames/60) % 60 hund = math.floor(frames/.6) % 100 + if frames == 15641 then + hund = math.ceil(frames/.6) % 100 + end str = string.format("%02d:%02d.%02d", min, sec, hund) return str end diff --git a/load/bgm.lua b/load/bgm.lua index 9e28b94..feb075d 100644 --- a/load/bgm.lua +++ b/load/bgm.lua @@ -6,14 +6,17 @@ bgm = { } local current_bgm = nil +local pitch = 1 local bgm_locked = false -local unfocused = false function switchBGM(sound, subsound) + if bgm_locked then + return + end if current_bgm ~= nil then current_bgm:stop() end - if bgm_locked or config.bgm_volume <= 0 then + if config.bgm_volume <= 0 then current_bgm = nil elseif sound ~= nil then if subsound ~= nil then @@ -67,25 +70,28 @@ function processBGMFadeout(dt) fadeout_time = 0 fading_bgm = false end - current_bgm:setVolume(fadeout_time * config.bgm_volume / total_fadeout_time) + current_bgm:setVolume( + fadeout_time * config.bgm_volume / total_fadeout_time + ) end end -function pauseBGM(f) - if f then - unfocused = true - end +function pauseBGM() if current_bgm ~= nil then current_bgm:pause() end end -function resumeBGM(f) - if f and scene.paused and unfocused then - unfocused = false - return - end +function resumeBGM() if current_bgm ~= nil then current_bgm:play() + current_bgm:setPitch(pitch) + end +end + +function pitchBGM(new_pitch) + pitch = new_pitch + if current_bgm ~= nil then + current_bgm:setPitch(pitch) end end diff --git a/load/fonts.lua b/load/fonts.lua index 36ae0c3..8a37d8d 100644 --- a/load/fonts.lua +++ b/load/fonts.lua @@ -26,8 +26,18 @@ font_3x5_4 = love.graphics.newImageFont( -4 ) -font_8x11 = love.graphics.newImageFont( - "res/fonts/8x11_medium.png", - "0123456789:.", +-- this would be font_8x11 with the other one as 8x11_2 +-- but that would break compatibility :( +font_8x11_small = love.graphics.newImageFont( + "res/fonts/8x11.png", + " 0123456789:;.,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .. + "?!/\\^@$%<=>()*-+[]_&", + 1 +) + +font_8x11 = love.graphics.newImageFont( + "res/fonts/8x11_medium.png", + " 0123456789:;.,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .. + "?!/\\^@$%<=>()*-+[]_&", 1 ) diff --git a/load/graphics.lua b/load/graphics.lua index 066ce72..154b886 100644 --- a/load/graphics.lua +++ b/load/graphics.lua @@ -1,31 +1,106 @@ -backgrounds = { - [0] = love.graphics.newImage("res/backgrounds/0.png"), - love.graphics.newImage("res/backgrounds/100.png"), - love.graphics.newImage("res/backgrounds/200.png"), - love.graphics.newImage("res/backgrounds/300.png"), - love.graphics.newImage("res/backgrounds/400.png"), - love.graphics.newImage("res/backgrounds/500.png"), - love.graphics.newImage("res/backgrounds/600.png"), - love.graphics.newImage("res/backgrounds/700.png"), - love.graphics.newImage("res/backgrounds/800.png"), - love.graphics.newImage("res/backgrounds/900.png"), - love.graphics.newImage("res/backgrounds/1000.png"), - love.graphics.newImage("res/backgrounds/1100.png"), - love.graphics.newImage("res/backgrounds/1200.png"), - love.graphics.newImage("res/backgrounds/1300.png"), - love.graphics.newImage("res/backgrounds/1400.png"), - love.graphics.newImage("res/backgrounds/1500.png"), - love.graphics.newImage("res/backgrounds/1600.png"), - love.graphics.newImage("res/backgrounds/1700.png"), - love.graphics.newImage("res/backgrounds/1800.png"), - love.graphics.newImage("res/backgrounds/1900.png"), - title = love.graphics.newImage("res/backgrounds/title.png"), - title_no_icon = love.graphics.newImage("res/backgrounds/title-no-icon.jpg"), - title_night = love.graphics.newImage("res/backgrounds/title-night.jpg"), - snow = love.graphics.newImage("res/backgrounds/snow.png"), - input_config = love.graphics.newImage("res/backgrounds/options-input.png"), - game_config = love.graphics.newImage("res/backgrounds/options-game.png"), +named_backgrounds = { + "title", "title_no_icon", "title_night", + "snow", "options_input", "options_game" } +current_playing_bgs = {} +extended_bgs = {} +image_formats = {".jpg", ".png"} +bgpath = "res/backgrounds/" +dir = love.filesystem.getDirectoryItems(bgpath) + +backgrounds = {} + +local function loadExtendedBgs() + extended_bgs = require("res.backgrounds.extend_section_bg") +end + +-- error handling for if there is no extend_section_bg +if pcall(loadExtendedBgs) then end + +-- helper method to populate backgrounds +local function createBackgroundIfExists(name, file_name) + local format_index = 1 + + -- see if background is an extension of another background + if extended_bgs[file_name] ~= nil then + copy_bg = extended_bgs[file_name] + copy_bg = copy_bg / 100 + backgrounds[name] = backgrounds[copy_bg] + return true + end + + -- try creating image backgrounds + while format_index <= #image_formats do + for num, existing_file in pairs(dir) do + if existing_file == (file_name..image_formats[format_index]) then + local tempBgPath = bgpath .. file_name .. image_formats[format_index] + backgrounds[name] = love.graphics.newImage(tempBgPath) + return true + end + end + format_index = format_index + 1 + end + + -- try creating video background + if love.filesystem.getInfo(bgpath .. file_name .. ".ogv") then + for num, existing_file in pairs(dir) do + if existing_file == (file_name..".ogv") then + local tempBgPath = bgpath .. file_name .. ".ogv" + backgrounds[name] = love.graphics.newVideo( + tempBgPath, {["audio"] = false} + ) + -- you can set audio to true, but the video will not loop + -- properly if audio extends beyond video frames + return true + end + end + end + return false +end + +local function stopOtherBgs(bg) + if #current_playing_bgs == 0 and bg:typeOf("Video") then + current_playing_bgs[#current_playing_bgs+1] = bg + end + + if #current_playing_bgs >= 1 then + while current_playing_bgs[1] ~= bg and #current_playing_bgs >= 1 do + current_playing_bgs[1]:pause() + current_playing_bgs[1]:rewind() + table.remove(current_playing_bgs, 1) + end + end + +end + +function fetchBackgroundAndLoop(id) + bg = backgrounds[id] + + if bg:typeOf("Video") and not bg:isPlaying() then + bg:rewind() + bg:play() + end + + stopOtherBgs(bg) + + return bg +end + +-- create section backgrounds +local section = 0 +while (createBackgroundIfExists(section, section*100)) do + section = section + 1 +end + +-- create named backgrounds +local nbgIndex = 1 +while nbgIndex <= #named_backgrounds do + createBackgroundIfExists( + named_backgrounds[nbgIndex], + string.gsub(named_backgrounds[nbgIndex], "_", "-") + ) + nbgIndex = nbgIndex + 1 +end -- in order, the colors are: -- red, orange, yellow, green, cyan, blue @@ -122,4 +197,17 @@ misc_graphics = { strike = love.graphics.newImage("res/img/strike.png"), santa = love.graphics.newImage("res/img/santa.png"), icon = love.graphics.newImage("res/img/cambridge_transparent.png") -} \ No newline at end of file +} + +-- utility function to allow any size background to be used +-- this will stretch the background to 4:3 aspect ratio +function drawBackground(id) + local bg_object = fetchBackgroundAndLoop(id) + local width = bg_object:getWidth() + local height = bg_object:getHeight() + love.graphics.draw( + bg_object, + 0, 0, 0, + 640 / width, 480 / height + ) +end \ No newline at end of file diff --git a/load/version.lua b/load/version.lua index 0328391..cca4f97 100644 --- a/load/version.lua +++ b/load/version.lua @@ -1 +1 @@ -version = "v0.3.1" \ No newline at end of file +version = "v0.3.4" \ No newline at end of file diff --git a/main.lua b/main.lua index f0e17c0..8abc042 100644 --- a/main.lua +++ b/main.lua @@ -1,5 +1,4 @@ function love.load() - math.randomseed(os.time()) highscores = {} love.graphics.setDefaultFilter("linear", "nearest") require "load.rpc" @@ -25,6 +24,11 @@ function love.load() -- used for screenshots GLOBAL_CANVAS = love.graphics.newCanvas() + -- aliasing to prevent people using math.random by accident + math.random = love.math.random + math.randomseed = love.math.setRandomSeed + math.randomseed(os.time()) + -- init config initConfig() config.depth_3d = 100 -- TODO add a setting for this to the menu @@ -111,8 +115,8 @@ function love.keypressed(key, scancode) saveConfig() scene.restart_message = true if config.secret then playSE("mode_decide") - else playSE("erase") end - -- f12 is reserved for saving screenshots + else playSE("erase", "single") end + -- f12 is reserved for saving screenshots elseif scancode == "f12" then local ss_name = os.date("ss/%Y-%m-%d_%H-%M-%S.png") local info = love.filesystem.getInfo("ss", "directory") @@ -278,22 +282,17 @@ function love.wheelmoved(x, y) scene:onInputPress({input=nil, type="wheel", x=x, y=y}) end -function love.focus(f) - if f then - resumeBGM(true) - else - pauseBGM(true) - end -end - function love.resize(w, h) - GLOBAL_CANVAS:release() - GLOBAL_CANVAS = love.graphics.newCanvas(w, h) + GLOBAL_CANVAS:release() + GLOBAL_CANVAS = love.graphics.newCanvas(w, h) end +-- higher values of TARGET_FPS will make the game run "faster" +-- since the game is mostly designed for 60 FPS local TARGET_FPS = 60 local FRAME_DURATION = 1.0 / TARGET_FPS +-- custom run function; optimizes game by syncing draw/update calls function love.run() if love.load then love.load(love.arg.parseGameArguments(arg), arg) end diff --git a/res/backgrounds/extend_section_bg.lua b/res/backgrounds/extend_section_bg.lua new file mode 100644 index 0000000..33dbb4c --- /dev/null +++ b/res/backgrounds/extend_section_bg.lua @@ -0,0 +1,14 @@ +-- ex: extend_section_bg[100] = 0 +-- extend_section_bg[200] = 0 +-- the video background associated with section 0 will continue playing into 100 and 200 without restarting. +-- will also cause any existing level 100, 200 backgrounds specified to NOT render. + +-- please also note that you cannot currently extend any "named" backgrounds, such as "title" and "options-input" + +extend_section_bg = {} + +-- extend_section_bg[100] = 0 +-- extend_section_bg[200] = 0 +-- remove the dashes + +return extend_section_bg \ No newline at end of file diff --git a/res/fonts/3x5.xcf b/res/fonts/3x5.xcf deleted file mode 100644 index 7b6fdcb..0000000 Binary files a/res/fonts/3x5.xcf and /dev/null differ diff --git a/res/fonts/8x11.png b/res/fonts/8x11.png index 2693141..7546e3d 100644 Binary files a/res/fonts/8x11.png and b/res/fonts/8x11.png differ diff --git a/res/fonts/8x11_medium.png b/res/fonts/8x11_medium.png index 54d170b..4ebcafc 100644 Binary files a/res/fonts/8x11_medium.png and b/res/fonts/8x11_medium.png differ diff --git a/res/fonts/8x12.xcf b/res/fonts/8x12.xcf deleted file mode 100644 index 746c877..0000000 Binary files a/res/fonts/8x12.xcf and /dev/null differ diff --git a/scene/credits.lua b/scene/credits.lua index 63fbc98..eacc9d5 100644 --- a/scene/credits.lua +++ b/scene/credits.lua @@ -16,9 +16,7 @@ function CreditsScene:new() end function CreditsScene:update() - if love.window.hasFocus() then - self.frames = self.frames + 1 - end + self.frames = self.frames + 1 if self.frames >= 2100 * self.scroll_speed then playSE("mode_decide") scene = TitleScene() @@ -32,11 +30,7 @@ function CreditsScene:render() local offset = self.frames / self.scroll_speed love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds[19], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground(19) love.graphics.setFont(font_3x5_4) love.graphics.print("Cambridge Credits", 320, 500 - offset) diff --git a/scene/game.lua b/scene/game.lua index e3015ea..21e4c91 100644 --- a/scene/game.lua +++ b/scene/game.lua @@ -25,6 +25,8 @@ function GameScene:new(game_mode, ruleset, inputs) hold=false, } self.paused = false + self.game.pause_count = 0 + self.game.pause_time = 0 DiscordRPC:update({ details = self.game.rpc_details, state = self.game.name, @@ -33,7 +35,9 @@ function GameScene:new(game_mode, ruleset, inputs) end function GameScene:update() - if love.window.hasFocus() and not self.paused then + if self.paused then + self.game.pause_time = self.game.pause_time + 1 + else local inputs = {} for input, value in pairs(self.inputs) do inputs[input] = value @@ -50,6 +54,16 @@ end function GameScene:render() self.game:draw(self.paused) + if self.game.pause_time > 0 or self.game.pause_count > 0 then + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setFont(font_3x5_2) + love.graphics.printf(string.format( + "%d PAUSE%s (%s)", + self.game.pause_count, + self.game.pause_count == 1 and "" or "S", + formatTime(self.game.pause_time) + ), 0, 0, 635, "right") + end end function GameScene:onInputPress(e) @@ -67,12 +81,17 @@ function GameScene:onInputPress(e) scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene() elseif e.input == "retry" then switchBGM(nil) + pitchBGM(1) self.game:onExit() scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then self.paused = not self.paused - if self.paused then pauseBGM() - else resumeBGM() end + if self.paused then + pauseBGM() + self.game.pause_count = self.game.pause_count + 1 + else + resumeBGM() + end elseif e.input == "menu_back" then self.game:onExit() scene = ModeSelectScene() diff --git a/scene/game_config.lua b/scene/game_config.lua index 2276ad6..68b4aff 100644 --- a/scene/game_config.lua +++ b/scene/game_config.lua @@ -45,11 +45,7 @@ end function ConfigScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["game_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_game") love.graphics.setFont(font_3x5_4) love.graphics.print("GAME SETTINGS", 80, 40) diff --git a/scene/input_config.lua b/scene/input_config.lua index 7b488f8..230c514 100644 --- a/scene/input_config.lua +++ b/scene/input_config.lua @@ -20,11 +20,7 @@ function ConfigScene:update() end function ConfigScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["input_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_input") love.graphics.setFont(font_3x5_4) love.graphics.print("INPUT CONFIG", 80, 40) diff --git a/scene/key_config.lua b/scene/key_config.lua index 419ebb5..a062b2e 100644 --- a/scene/key_config.lua +++ b/scene/key_config.lua @@ -45,11 +45,7 @@ end function KeyConfigScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["input_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_input") love.graphics.setFont(font_3x5_2) for i, input in ipairs(configurable_inputs) do diff --git a/scene/mode_select.lua b/scene/mode_select.lua index b394896..329de3c 100755 --- a/scene/mode_select.lua +++ b/scene/mode_select.lua @@ -58,11 +58,7 @@ function ModeSelectScene:update() end function ModeSelectScene:render() - love.graphics.draw( - backgrounds[0], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground(0) love.graphics.draw(misc_graphics["select_mode"], 20, 40) diff --git a/scene/replay.lua b/scene/replay.lua index f1ca2c0..d6f7d18 100644 --- a/scene/replay.lua +++ b/scene/replay.lua @@ -6,6 +6,9 @@ ReplayScene.title = "Replay" function ReplayScene:new(replay, game_mode, ruleset) config.gamesettings = replay["gamesettings"] + if replay["delayed_auto_shift"] then config.das = replay["delayed_auto_shift"] end + if replay["auto_repeat_rate"] then config.arr = replay["auto_repeat_rate"] end + if replay["das_cut_delay"] then config.dcd = replay["das_cut_delay"] end love.math.setRandomSeed(replay["random_low"], replay["random_high"]) love.math.setRandomState(replay["random_state"]) self.retry_replay = replay @@ -29,8 +32,13 @@ function ReplayScene:new(replay, game_mode, ruleset) hold=false, } self.paused = false + self.game.pause_count = replay["pause_count"] + self.game.pause_time = replay["pause_time"] self.replay = deepcopy(replay) self.replay_index = 1 + self.replay_speed = 1 + self.show_invisible = false + self.frame_steps = 0 DiscordRPC:update({ details = "Viewing a replay", state = self.game.name, @@ -39,18 +47,25 @@ function ReplayScene:new(replay, game_mode, ruleset) end function ReplayScene:update() - if love.window.hasFocus() and not self.paused then - self.inputs = self.replay["inputs"][self.replay_index]["inputs"] - self.replay["inputs"][self.replay_index]["frames"] = self.replay["inputs"][self.replay_index]["frames"] - 1 - if self.replay["inputs"][self.replay_index]["frames"] == 0 and self.replay_index < table.getn(self.replay["inputs"]) then - self.replay_index = self.replay_index + 1 + local frames_left = self.replay_speed + if not self.paused or self.frame_steps > 0 then + if self.frame_steps > 0 then + self.frame_steps = self.frame_steps - 1 end - local input_copy = {} - for input, value in pairs(self.inputs) do - input_copy[input] = value + while frames_left > 0 do + frames_left = frames_left - 1 + self.inputs = self.replay["inputs"][self.replay_index]["inputs"] + self.replay["inputs"][self.replay_index]["frames"] = self.replay["inputs"][self.replay_index]["frames"] - 1 + if self.replay["inputs"][self.replay_index]["frames"] == 0 and self.replay_index < table.getn(self.replay["inputs"]) then + self.replay_index = self.replay_index + 1 + end + local input_copy = {} + for input, value in pairs(self.inputs) do + input_copy[input] = value + end + self.game:update(input_copy, self.ruleset) + self.game.grid:update() end - self.game:update(input_copy, self.ruleset) - self.game.grid:update() DiscordRPC:update({ details = "Viewing a replay", state = self.game.name, @@ -64,6 +79,30 @@ function ReplayScene:render() love.graphics.setColor(1, 1, 1, 1) love.graphics.setFont(font_3x5_3) love.graphics.printf("REPLAY", 0, 0, 635, "right") + local pauses_y_coordinate = 23 + if self.replay_speed > 1 then + pauses_y_coordinate = pauses_y_coordinate + 20 + love.graphics.printf(self.replay_speed.."X", 0, 20, 635, "right") + end + love.graphics.setFont(font_3x5_2) + if self.game.pause_time and self.game.pause_count then + if self.game.pause_time > 0 or self.game.pause_count > 0 then + love.graphics.printf(string.format( + "%d PAUSE%s (%s)", + self.game.pause_count, + self.game.pause_count == 1 and "" or "S", + formatTime(self.game.pause_time) + ), 0, pauses_y_coordinate, 635, "right") + end + else + love.graphics.printf("?? PAUSES (--:--.--)", 0, pauses_y_coordinate, 635, "right") + end + if self.show_invisible then + self.game.grid:draw() + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setFont(font_3x5_3) + love.graphics.printf("SHOW INVIS", 64, 60, 160, "center") + end end function ReplayScene:onInputPress(e) @@ -72,6 +111,8 @@ function ReplayScene:onInputPress(e) e.input == "menu_decide" or e.input == "retry" ) then + switchBGM(nil) + pitchBGM(1) self.game:onExit() loadSave() love.math.setRandomSeed(os.time()) @@ -86,6 +127,23 @@ function ReplayScene:onInputPress(e) self.paused = not self.paused if self.paused then pauseBGM() else resumeBGM() end + --frame step + elseif e.input == "rotate_left" then + self.frame_steps = self.frame_steps + 1 + elseif e.input == "left" then + self.replay_speed = self.replay_speed - 1 + if self.replay_speed < 1 then + self.replay_speed = 1 + end + pitchBGM(self.replay_speed) + elseif e.input == "right" then + self.replay_speed = self.replay_speed + 1 + if self.replay_speed > 99 then + self.replay_speed = 99 + end + pitchBGM(self.replay_speed) + elseif e.input == "hold" then + self.show_invisible = not self.show_invisible end end diff --git a/scene/replay_select.lua b/scene/replay_select.lua index 7ae3140..4101075 100644 --- a/scene/replay_select.lua +++ b/scene/replay_select.lua @@ -14,8 +14,12 @@ function ReplaySelectScene:new() replay_file_list = love.filesystem.getDirectoryItems("replays") for i=1,#replay_file_list do local data = love.filesystem.read("replays/"..replay_file_list[i]) - local new_replay = binser.deserialize(data)[1] - replays[#replays + 1] = new_replay + local success, new_replay = pcall( + function() return binser.deserialize(data)[1] end + ) + if success then + replays[#replays + 1] = new_replay + end end table.sort(replays, function(a, b) return a["timestamp"] > b["timestamp"] @@ -43,8 +47,6 @@ function ReplaySelectScene:new() end function ReplaySelectScene:update() - switchBGM(nil) -- experimental - if self.das_up or self.das_down or self.das_left or self.das_right then self.das = self.das + 1 else @@ -74,16 +76,12 @@ function ReplaySelectScene:update() end function ReplaySelectScene:render() - love.graphics.draw( - backgrounds[0], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground(0) -- Same graphic as mode select --love.graphics.draw(misc_graphics["select_mode"], 20, 40) - love.graphics.setFont(font_3x5_4) + love.graphics.setFont(font_8x11) love.graphics.print("SELECT REPLAY", 20, 35) if self.display_warning then diff --git a/scene/settings.lua b/scene/settings.lua index eaa7361..a51d8c1 100644 --- a/scene/settings.lua +++ b/scene/settings.lua @@ -29,11 +29,7 @@ function SettingsScene:update() end function SettingsScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["game_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_game") love.graphics.setFont(font_3x5_4) love.graphics.print("SETTINGS", 80, 40) diff --git a/scene/stick_config.lua b/scene/stick_config.lua index 3e7be01..d8bc03c 100644 --- a/scene/stick_config.lua +++ b/scene/stick_config.lua @@ -46,11 +46,7 @@ end function StickConfigScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["input_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_input") love.graphics.setFont(font_3x5_2) for i, input in ipairs(configurable_inputs) do diff --git a/scene/title.lua b/scene/title.lua index a7973c5..c2c54db 100644 --- a/scene/title.lua +++ b/scene/title.lua @@ -74,11 +74,7 @@ local block_offsets = { function TitleScene:render() love.graphics.setFont(font_3x5_4) love.graphics.setColor(1, 1, 1, 1 - self.snow_bg_opacity) - love.graphics.draw( - backgrounds["title_no_icon"], -- title, title_night - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("title_no_icon") -- title, title_night -- 490, 192 for _, b in ipairs(block_offsets) do @@ -109,11 +105,7 @@ function TitleScene:render() love.graphics.setFont(font_3x5_2) love.graphics.setColor(1, 1, 1, self.snow_bg_opacity) - love.graphics.draw( - backgrounds["snow"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("snow") love.graphics.draw( misc_graphics["santa"], diff --git a/scene/tuning.lua b/scene/tuning.lua index ef0f4ee..42bc65a 100644 --- a/scene/tuning.lua +++ b/scene/tuning.lua @@ -34,11 +34,7 @@ end function TuningScene:render() love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds["game_config"], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground("options_game") love.graphics.setColor(1, 1, 1, 0.5) love.graphics.rectangle("fill", 75, 98 + self.highlight * 75, 400, 33) diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..aa7685b --- /dev/null +++ b/start.bat @@ -0,0 +1,7 @@ +@echo OFF + +rem This solution of detecting the current CPU taken from here: https://stackoverflow.com/a/24590583 +reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set CURRENT_CPU=32BIT || set CURRENT_CPU=64BIT + +if %CURRENT_CPU%==32BIT .\dist\win32\love.exe . +if %CURRENT_CPU%==64BIT .\dist\windows\love.exe . \ No newline at end of file diff --git a/start_win32.bat b/start_win32.bat deleted file mode 100644 index 5d2ac86..0000000 --- a/start_win32.bat +++ /dev/null @@ -1 +0,0 @@ -dist\win32\love.exe . \ No newline at end of file diff --git a/start_win64.bat b/start_win64.bat deleted file mode 100644 index acdd477..0000000 --- a/start_win64.bat +++ /dev/null @@ -1 +0,0 @@ -dist\windows\love.exe . \ No newline at end of file diff --git a/tetris/components/grid.lua b/tetris/components/grid.lua index c4659c3..ad388f6 100644 --- a/tetris/components/grid.lua +++ b/tetris/components/grid.lua @@ -232,12 +232,15 @@ function Grid:applyBigPiece(piece) end end -function Grid:checkForBravo(cleared_row_count) - for i = 0, self.height - 1 - cleared_row_count do - for j = 0, self.width - 1 do - if self:isOccupied(j, i) then return false end - end +-- places where you see this take an argument used the old, buggy method +function Grid:checkForBravo() + for i = 0, self.height - 1 do + if not self:isRowFull(i+1) then + for j = 0, self.width - 1 do + if self:isOccupied(j, i) then return false end + end end + end return true end diff --git a/tetris/modes/big_a2.lua b/tetris/modes/big_a2.lua index 73d8034..51d06ff 100755 --- a/tetris/modes/big_a2.lua +++ b/tetris/modes/big_a2.lua @@ -10,41 +10,10 @@ BigA2Game.tagline = "Big blocks in the most celebrated TGM mode!" function BigA2Game:new() BigA2Game.super:new() self.big_mode = true -end - -function BigA2Game:updateScore(level, drop_bonus, cleared_lines) - cleared_lines = cleared_lines / 2 - if not self.clear then - self:updateGrade(cleared_lines) - if cleared_lines >= 4 then - self.tetris_count = self.tetris_count + 1 - end - if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end - if cleared_lines > 0 then - self.combo = self.combo + (cleared_lines - 1) * 2 - 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 BigA2Game: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 - self.grid:clear() - if self:qualifiesForMRoll() then self.grade = 32 end - self.roll_frames = -150 + local getClearedRowCount = self.grid.getClearedRowCount + self.grid.getClearedRowCount = function(self) + return getClearedRowCount(self) / 2 end - self.lock_drop = self.level >= 900 - self.lock_hard_drop = self.level >= 900 end return BigA2Game \ No newline at end of file diff --git a/tetris/modes/gamemode.lua b/tetris/modes/gamemode.lua index 5d38e3c..1da020e 100644 --- a/tetris/modes/gamemode.lua +++ b/tetris/modes/gamemode.lua @@ -136,25 +136,27 @@ function GameMode:saveReplay() replay["lines"] = self.lines replay["gamesettings"] = config.gamesettings replay["secret_inputs"] = self.secret_inputs + replay["delayed_auto_shift"] = config.das + replay["auto_repeat_rate"] = config.arr + replay["das_cut_delay"] = config.dcd replay["timestamp"] = os.time() + replay["pause_count"] = self.pause_count + replay["pause_time"] = self.pause_time if love.filesystem.getInfo("replays") == nil then love.filesystem.createDirectory("replays") end - local replay_files = love.filesystem.getDirectoryItems("replays") - -- Select replay filename that doesn't collide with an existing one + local init_name = string.format("replays/%s.crp", os.date("%Y-%m-%d_%H-%M-%S")) + local replay_name = init_name local replay_number = 0 - local collision = true - while collision do - collision = false - replay_number = replay_number + 1 - for key, file in pairs(replay_files) do - if file == replay_number..".crp" then - collision = true - break - end + while true do + if love.filesystem.getInfo(replay_name, "file") then + replay_number = replay_number + 1 + replay_name = string.format("%s (%d)", init_name, replay_number) + else + break end end - love.filesystem.write("replays/"..replay_number..".crp", binser.serialize(replay)) + love.filesystem.write(replay_name, binser.serialize(replay)) end function GameMode:addReplayInput(inputs) @@ -178,6 +180,9 @@ function GameMode:update(inputs, ruleset) if self.game_over or self.completed then if self.save_replay and self.game_over_frames == 0 then self:saveReplay() + + -- ensure replays are only saved once per game, incase self.game_over_frames == 0 for longer than one frame + self.save_replay = false end self.game_over_frames = self.game_over_frames + 1 return @@ -399,6 +404,7 @@ end function GameMode:onGameOver() switchBGM(nil) + pitchBGM(1) local alpha = 0 local animation_length = 120 if self.game_over_frames < animation_length then @@ -981,12 +987,10 @@ function GameMode:drawSectionTimesWithSplits(current_section, section_limit) end function GameMode:drawBackground() + local id = self:getBackground() + if type(id) == "number" then id = clamp(id, 0, #backgrounds) end love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw( - backgrounds[self:getBackground()], - 0, 0, 0, - 0.5, 0.5 - ) + drawBackground(id) end function GameMode:drawFrame() diff --git a/tetris/modes/marathon_2020.lua b/tetris/modes/marathon_2020.lua index 7994ecf..54d1c3b 100644 --- a/tetris/modes/marathon_2020.lua +++ b/tetris/modes/marathon_2020.lua @@ -283,7 +283,7 @@ function Marathon2020Game:sectionPassed(old_level, new_level) ) else return ( - (new_level < 2001 and math.floor(old_level / 100) < math.floor(new_level / 100)) or + (new_level < 2000 and math.floor(old_level / 100) < math.floor(new_level / 100)) or (new_level >= 2020) ) end @@ -353,15 +353,10 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level) self.section_start_time = self.frames if ( - self.section_status[section - 1] == "cool" and - self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120 and - self.secondary_section_times[section] < cool_cutoffs[self.delay_level] + (self.secondary_section_times[section] < cool_cutoffs[self.delay_level]) and + (section == 1 or self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120) ) then sectionCool(section) - elseif self.section_status[section - 1] == "cool" then - table.insert(self.section_status, "none") - elseif self.secondary_section_times[section] < cool_cutoffs[self.delay_level] then - sectionCool(section) else table.insert(self.section_status, "none") end diff --git a/tetris/modes/marathon_a2.lua b/tetris/modes/marathon_a2.lua index 08fccd3..906c87f 100644 --- a/tetris/modes/marathon_a2.lua +++ b/tetris/modes/marathon_a2.lua @@ -249,10 +249,13 @@ local grade_conversion = { } function MarathonA2Game:whilePieceActive() - self.grade_point_decay_counter = self.grade_point_decay_counter + 1 - if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then - self.grade_point_decay_counter = 0 - self.grade_points = math.max(0, self.grade_points - 1) + if self.clear then return + else + self.grade_point_decay_counter = self.grade_point_decay_counter + 1 + if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then + self.grade_point_decay_counter = 0 + self.grade_points = math.max(0, self.grade_points - 1) + end end end diff --git a/tetris/modes/marathon_a3.lua b/tetris/modes/marathon_a3.lua index 2c2890c..e40d373 100644 --- a/tetris/modes/marathon_a3.lua +++ b/tetris/modes/marathon_a3.lua @@ -26,7 +26,9 @@ function MarathonA3Game:new() self.roll_points = 0 self.grade_point_decay_counter = 0 self.section_cool_grade = 0 - self.section_status = { [0] = "none" } + --self.section_status = { [0] = "none" } + self.section_cools = { [0] = 0 } + self.section_regrets = { [0] = 0 } self.section_start_time = 0 self.secondary_section_times = { [0] = 0 } self.section_times = { [0] = 0 } @@ -201,15 +203,23 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level) 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") + if self.grade > 0 then + --this happens after the points are added, intentionally + local currentgrade = self:getAggregateGrade() + while self:getAggregateGrade() >= currentgrade do + self.grade = self.grade - 1 + end + self.grade_points = 0 + end + table.insert(self.section_regrets, 1) self.coolregret_message = "REGRET!!" self.coolregret_timer = 300 - elseif self.section_cool then - self.section_cool_grade = self.section_cool_grade + 1 - table.insert(self.section_status, "cool") else - table.insert(self.section_status, "none") + table.insert(self.section_regrets, 0) + end + + if self.section_cool then + self.section_cool_grade = self.section_cool_grade + 1 end self.section_cool = false @@ -223,6 +233,9 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level) self.section_cool = true self.coolregret_message = "COOL!!" self.coolregret_timer = 300 + table.insert(self.section_cools, 1) + else + table.insert(self.section_cools, 0) end end end @@ -402,9 +415,11 @@ MarathonA3Game.mRollOpacityFunction = function(age) end function MarathonA3Game:sectionColourFunction(section) - if self.section_status[section] == "cool" then + if self.section_cools[section] == 1 and self.section_regrets[section] == 1 then + return { 1, 1, 0, 1 } + elseif self.section_cools[section] == 1 then return { 0, 1, 0, 1 } - elseif self.section_status[section] == "regret" then + elseif self.section_regrets[section] == 1 then return { 1, 0, 0, 1 } else return { 1, 1, 1, 1 } diff --git a/tetris/modes/phantom_mania.lua b/tetris/modes/phantom_mania.lua index 5bab937..75bf800 100644 --- a/tetris/modes/phantom_mania.lua +++ b/tetris/modes/phantom_mania.lua @@ -28,7 +28,6 @@ function PhantomManiaGame:new() self.combo = 1 self.tetrises = 0 self.section_tetrises = {[0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - self.section_req = true self.randomizer = History6RollsRandomizer() end @@ -55,7 +54,7 @@ function PhantomManiaGame:getDasLimit() end function PhantomManiaGame:getLineClearDelay() - return self:getLineARE() + return self:getLineARE() - 2 end function PhantomManiaGame:getLockDelay() @@ -120,11 +119,6 @@ function PhantomManiaGame:onLineClear(cleared_row_count) self.level = 999 end self.clear = true - for i = 0, 9 do - if self.section_tetrises[i] < (i == 9 and 1 or 2) then - self.section_req = false - end - end else self.level = new_level end @@ -175,6 +169,10 @@ local function getLetterGrade(level, clear) end end +function PhantomManiaGame:qualifiesForGM() + return true +end + function PhantomManiaGame:drawScoringInfo() PhantomManiaGame.super.drawScoringInfo(self) @@ -195,7 +193,7 @@ function PhantomManiaGame:drawScoringInfo() if getLetterGrade(self.level, self.clear) ~= "" then if self.roll_frames > 1982 then love.graphics.setColor(1, 0.5, 0, 1) elseif self.level == 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end - if self.level == 999 and self.section_req and self.tetrises >= 31 then + if self.level == 999 and self:qualifiesForGM() then love.graphics.printf("GM", text_x, 140, 90, "left") else love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") diff --git a/tetris/modes/phantom_mania2.lua b/tetris/modes/phantom_mania2.lua index 12ca98b..2f655f8 100644 --- a/tetris/modes/phantom_mania2.lua +++ b/tetris/modes/phantom_mania2.lua @@ -41,6 +41,7 @@ function PhantomMania2Game:new() self.coolregret_message = "" self.coolregret_timer = 0 + self.coolregrets = { [0] = 0 } end function PhantomMania2Game:getARE() @@ -202,13 +203,13 @@ end local cool_cutoffs = { frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), - frameTime(0,27), frameTime(0,27), frameTime(0,27), + frameTime(0,30), frameTime(0,30), frameTime(0,30), } local regret_cutoffs = { frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), - frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40), - frameTime(0,35), frameTime(0,35), frameTime(0,35), + frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,42), + frameTime(0,42), frameTime(0,42), frameTime(0,42), } function PhantomMania2Game:updateSectionTimes(old_level, new_level) @@ -219,11 +220,14 @@ 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 + table.insert(self.coolregrets, 2) self.coolregret_message = "COOL!!" self.coolregret_timer = 300 elseif section_time <= regret_cutoffs[section] then self.grade = self.grade + 1 + table.insert(self.coolregrets, 1) else + table.insert(self.coolregrets, 0) self.coolregret_message = "REGRET!!" self.coolregret_timer = 300 end @@ -292,6 +296,16 @@ function PhantomMania2Game:setHoldOpacity() end end +function PhantomMania2Game:sectionColourFunction(section) + if self.coolregrets[section] == 2 then + return { 0, 1, 0, 1 } + elseif self.coolregrets[section] == 0 then + return { 1, 0, 0, 1 } + else + return { 1, 1, 1, 1 } + end +end + function PhantomMania2Game:drawScoringInfo() PhantomMania2Game.super.drawScoringInfo(self) diff --git a/tetris/modes/phantom_mania_n.lua b/tetris/modes/phantom_mania_n.lua index c063745..74b026a 100644 --- a/tetris/modes/phantom_mania_n.lua +++ b/tetris/modes/phantom_mania_n.lua @@ -13,4 +13,14 @@ function PhantomManiaNGame:new() self.enable_hold = true end +function PhantomManiaNGame:qualifiesForGM() + if self.tetrises < 31 then return false end + for i = 0, 9 do + if self.section_tetrises[i] < (i == 9 and 1 or 2) then + return false + end + end + return true +end + return PhantomManiaNGame diff --git a/tetris/modes/survival_2020.lua b/tetris/modes/survival_2020.lua index 76e7fa3..dd9f9d0 100644 --- a/tetris/modes/survival_2020.lua +++ b/tetris/modes/survival_2020.lua @@ -143,7 +143,7 @@ end function Survival2020Game:onPieceEnter() if not self.clear and ( (self.level < 1900 and self.level % 100 ~= 99) or - self.level == 2019 + (1900 <= self.level and self.level < 2019) ) then self.level = self.level + 1 end @@ -201,7 +201,7 @@ end Survival2020Game.opacityFunction = function(age) if age > 300 then return 0 - else return 1 - Math.max(age - 240, 0) / 60 end + else return 1 - math.max(age - 240, 0) / 60 end end function Survival2020Game:drawGrid() @@ -249,7 +249,7 @@ function Survival2020Game:drawScoringInfo() end function Survival2020Game:getBackground() - return math.floor(self.level / 100) + return math.min(19, math.floor(self.level / 100)) end function Survival2020Game:getHighscoreData() diff --git a/tetris/rulesets/arika_exp.lua b/tetris/rulesets/arika_exp.lua new file mode 100644 index 0000000..bd5aa6c --- /dev/null +++ b/tetris/rulesets/arika_exp.lua @@ -0,0 +1,49 @@ +local Piece = require 'tetris.components.piece' +local Ruleset = require 'tetris.rulesets.arika_ace2' + +local ARS = Ruleset:extend() + +ARS.name = "ARS-X" +ARS.hash = "ArikaEXP" + +ARS.MANIPULATIONS_MAX = 24 +ARS.ROTATIONS_MAX = 12 + +function ARS:onPieceCreate(piece, grid) + piece.manipulations = 0 + piece.rotations = 0 + piece.lowest_y = -math.huge +end + +function ARS:checkNewLow(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.rotations = 0 + piece.lowest_y = y + end + end +end + +function ARS:onPieceMove(piece, grid) + piece.lock_delay = 0 -- move reset + if piece:isDropBlocked(grid) then + piece.manipulations = piece.manipulations + 1 + if piece.manipulations >= ARS.MANIPULATIONS_MAX then + piece.locked = true + end + end +end + +function ARS:onPieceRotate(piece, grid, upward) + piece.lock_delay = 0 -- rotate reset + if upward or piece:isDropBlocked(grid) then + piece.rotations = piece.rotations + 1 + if piece.rotations >= ARS.ROTATIONS_MAX and piece:isDropBlocked(grid) then + piece.locked = true + end + end +end + +return ARS \ No newline at end of file diff --git a/tetris/rulesets/standard.lua b/tetris/rulesets/standard.lua index 79226d7..0013277 100644 --- a/tetris/rulesets/standard.lua +++ b/tetris/rulesets/standard.lua @@ -1,5 +1,5 @@ local Piece = require 'tetris.components.piece' -local Ruleset = require 'tetris.rulesets.standard_exp' +local Ruleset = require 'tetris.rulesets.standard_ace' local SRS = Ruleset:extend() @@ -8,6 +8,8 @@ SRS.hash = "Standard" SRS.softdrop_lock = false SRS.harddrop_lock = true +SRS.enable_IRS_wallkicks = true + SRS.MANIPULATIONS_MAX = 15 SRS.wallkicks_line = { @@ -33,8 +35,8 @@ SRS.wallkicks_line = { }, }; -function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid) +function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid) local kicks if piece.shape == "O" then return @@ -69,6 +71,12 @@ function SRS:checkNewLow(piece) end end +function SRS:onPieceCreate(piece, grid) + piece.manipulations = 0 + piece.rotations = 0 + piece.lowest_y = -math.huge +end + function SRS:onPieceDrop(piece, grid) self:checkNewLow(piece) if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then @@ -85,6 +93,8 @@ function SRS:onPieceMove(piece, grid) if piece.manipulations >= SRS.MANIPULATIONS_MAX then piece.locked = true end + else + piece.locked = false end end @@ -102,4 +112,6 @@ end function SRS:canPieceRotate() return true end +function SRS:get180RotationValue() return 2 end + return SRS diff --git a/tetris/rulesets/arika_srs.lua b/tetris/rulesets/standard_ace.lua old mode 100755 new mode 100644 similarity index 93% rename from tetris/rulesets/arika_srs.lua rename to tetris/rulesets/standard_ace.lua index 784c23d..15a68f6 --- a/tetris/rulesets/arika_srs.lua +++ b/tetris/rulesets/standard_ace.lua @@ -1,5 +1,5 @@ local Piece = require 'tetris.components.piece' -local Ruleset = require 'tetris.rulesets.ti_srs' +local Ruleset = require 'tetris.rulesets.standard_ti' local SRS = Ruleset:extend() diff --git a/tetris/rulesets/standard_exp.lua b/tetris/rulesets/standard_exp.lua index b36192a..354f90d 100755 --- a/tetris/rulesets/standard_exp.lua +++ b/tetris/rulesets/standard_exp.lua @@ -1,5 +1,5 @@ local Piece = require 'tetris.components.piece' -local Ruleset = require 'tetris.rulesets.arika_srs' +local Ruleset = require 'tetris.rulesets.standard_ti' local SRS = Ruleset:extend() @@ -27,8 +27,6 @@ function SRS:checkNewLow(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.rotations = 0 piece.lowest_y = y end end diff --git a/tetris/rulesets/ti_srs.lua b/tetris/rulesets/standard_ti.lua similarity index 100% rename from tetris/rulesets/ti_srs.lua rename to tetris/rulesets/standard_ti.lua