diff --git a/load/graphics.lua b/load/graphics.lua index e351b17..df76987 100644 --- a/load/graphics.lua +++ b/load/graphics.lua @@ -118,7 +118,8 @@ misc_graphics = { ready = love.graphics.newImage("res/img/ready.png"), go = love.graphics.newImage("res/img/go.png"), select_mode = love.graphics.newImage("res/img/select_mode.png"), + select_challenge = love.graphics.newImage("res/img/select_challenge_placeholder.png"), 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 +} diff --git a/main.lua b/main.lua index f54b3c1..57ae2dd 100644 --- a/main.lua +++ b/main.lua @@ -13,14 +13,14 @@ function love.load() loadSave() require "funcs" require "scene" - + --config["side_next"] = false --config["reverse_rotate"] = true --config["das_last_key"] = false --config["fullscreen"] = false love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); - + -- used for screenshots GLOBAL_CANVAS = love.graphics.newCanvas() @@ -42,6 +42,13 @@ function initModules() game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5)) end end + challenges = {} + challenge_list = love.filesystem.getDirectoryItems("tetris/challenges") + for i=1,#challenge_list do + if(challenge_list[i] ~= "challenge.lua" and string.sub(challenge_list[i], -4) == ".lua") then + challenges[#challenges+1] = require ("tetris.challenges."..string.sub(challenge_list[i],1,-5)) + end + end rulesets = {} rule_list = love.filesystem.getDirectoryItems("tetris/rulesets") for i=1,#rule_list do @@ -72,7 +79,7 @@ function love.draw() (height - scale_factor * 480) / 2 ) love.graphics.scale(scale_factor) - + scene:render() if config.gamesettings.display_gamemode == 1 or scene.title == "Title" then @@ -83,9 +90,9 @@ function love.draw() "fps - " .. version, 0, 460, 635, "right" ) end - + love.graphics.pop() - + love.graphics.setCanvas() love.graphics.setColor(1,1,1,1) love.graphics.draw(GLOBAL_CANVAS) @@ -120,7 +127,7 @@ function love.keypressed(key, scancode) GLOBAL_CANVAS:newImageData():encode("png", ss_name) -- function keys are reserved elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then - return + return -- escape is reserved for menu_back elseif scancode == "escape" then scene:onInputPress({input="menu_back", type="key", key=key, scancode=scancode}) @@ -140,7 +147,7 @@ function love.keyreleased(key, scancode) 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 + 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 @@ -186,7 +193,7 @@ function love.joystickaxis(joystick, axis, value) config.input.joysticks and config.input.joysticks[joystick:getName()] and config.input.joysticks[joystick:getName()].axes and - config.input.joysticks[joystick:getName()].axes[axis] + config.input.joysticks[joystick:getName()].axes[axis] then if math.abs(value) >= 1 then input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"] @@ -314,10 +321,10 @@ function love.run() if love.timer then processBGMFadeout(love.timer.step()) end - + if scene and scene.update and love.timer then scene:update() - + local frame_duration = 1.0 / TARGET_FPS if time_accumulator < frame_duration then if love.graphics and love.graphics.isActive() and love.draw then diff --git a/res/img/select_challenge_placeholder.png b/res/img/select_challenge_placeholder.png new file mode 100644 index 0000000..644d6fb Binary files /dev/null and b/res/img/select_challenge_placeholder.png differ diff --git a/scene.lua b/scene.lua index aaa81b8..30246eb 100644 --- a/scene.lua +++ b/scene.lua @@ -10,7 +10,9 @@ function Scene:onInputRelease() end ExitScene = require "scene.exit" GameScene = require "scene.game" +ChallengeScene = require "scene.challenge" ModeSelectScene = require "scene.mode_select" +ChallengeSelectScene = require "scene.challenge_select" KeyConfigScene = require "scene.key_config" StickConfigScene = require "scene.stick_config" InputConfigScene = require "scene.input_config" diff --git a/scene/challenge.lua b/scene/challenge.lua new file mode 100644 index 0000000..5fadce7 --- /dev/null +++ b/scene/challenge.lua @@ -0,0 +1,93 @@ +local ChallengeScene = Scene:extend() + +ChallengeScene.title = "Challenge" + +require 'load.save' + +function ChallengeScene:new(game_mode, ruleset, inputs) + self.retry_mode = game_mode + self.retry_ruleset = ruleset + self.secret_inputs = inputs + self.game = game_mode(self.secret_inputs) + self.ruleset = ruleset(self.game) + 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, + } + self.paused = false + DiscordRPC:update({ + details = "In challenge", + state = self.game.name, + largeImageKey = "ingame-"..self.game:getBackground().."00" + }) +end + +function ChallengeScene:update() + if love.window.hasFocus() and not self.paused then + local inputs = {} + for input, value in pairs(self.inputs) do + inputs[input] = value + end + self.game:update(inputs, self.ruleset) + self.game.grid:update() + DiscordRPC:update({ + largeImageKey = "ingame-"..self.game:getBackground().."00" + }) + end +end + +function ChallengeScene:render() + self.game:draw(self.paused) +end + +function ChallengeScene:onInputPress(e) + if ( + self.game.game_over or self.game.completed + ) and ( + e.input == "menu_decide" or + e.input == "menu_back" or + e.input == "retry" + ) then + highscore_entry = self.game:getHighscoreData() + highscore_hash = self.game.hash .. "-" .. self.ruleset.hash + submitHighscore(highscore_hash, highscore_entry) + self.game:onExit() + scene = e.input == "retry" and ChallengeScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene() + elseif e.input == "retry" then + switchBGM(nil) + self.game:onExit() + scene = ChallengeScene(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 + elseif e.input == "menu_back" then + self.game:onExit() + scene = ChallengeSelectScene() + elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then + self.inputs[e.input] = true + end +end + +function ChallengeScene:onInputRelease(e) + if e.input and string.sub(e.input, 1, 5) ~= "menu_" then + self.inputs[e.input] = false + end +end + +function submitHighscore(hash, data) + if not highscores[hash] then highscores[hash] = {} end + table.insert(highscores[hash], data) + saveHighscores() +end + +return ChallengeScene diff --git a/scene/challenge_select.lua b/scene/challenge_select.lua new file mode 100644 index 0000000..94c86b3 --- /dev/null +++ b/scene/challenge_select.lua @@ -0,0 +1,182 @@ +local ChallengeSelectScene = Scene:extend() + +ChallengeSelectScene.title = "Challenges" + +current_challenge = 1 + +function indexOf(array, value) + for i, v in ipairs(array) do + if v == value then + return i + end + end + return nil +end + +function ChallengeSelectScene:new() + -- reload custom modules + initModules() + if table.getn(challenges) == 0 then + self.display_warning = true + current_challenge = 1 + else + self.display_warning = false + if current_challenge > table.getn(challenges) then + current_challenge = 1 + end + end + + self.menu_state = { + challenge = current_challenge, + select = "challenge", + } + self.secret_inputs = {} + self.das = 0 + DiscordRPC:update({ + details = "In menus", + state = "Choosing a challenge", + largeImageKey = "ingame-000" + }) +end + +function ChallengeSelectScene:update() + switchBGM(nil) -- experimental + + if self.das_up or self.das_down then + self.das = self.das + 1 + else + self.das = 0 + end + + if self.das >= 15 then + self:changeOption(self.das_up and -1 or 1) + self.das = self.das - 4 + end + + DiscordRPC:update({ + details = "In menus", + state = "Choosing a challenge", + largeImageKey = "ingame-000" + }) +end + +function ChallengeSelectScene:render() + love.graphics.draw( + backgrounds[0], + 0, 0, 0, + 0.5, 0.5 + ) + + love.graphics.draw(misc_graphics["select_challenge"], 20, 40) + + if self.display_warning then + love.graphics.setFont(font_3x5_3) + love.graphics.printf( + "You have no challenges", + 80, 200, 480, "center" + ) + love.graphics.setFont(font_3x5_2) + love.graphics.printf( + "Come back to this menu after getting more challenges. " .. + "Press any button to return to the main menu.", + 80, 250, 480, "center" + ) + return + end + + if self.menu_state.select == "challenge" then + love.graphics.setColor(1, 1, 1, 0.5) + end + love.graphics.rectangle("fill", 20, 258, 240, 22) + + if self.menu_state.select == "challenge" then + love.graphics.setColor(1, 1, 1, 0.25) + end + + love.graphics.setColor(1, 1, 1, 1) + + love.graphics.setFont(font_3x5_2) + for idx, challenge in pairs(challenges) do + if(idx >= self.menu_state.challenge-9 and idx <= self.menu_state.challenge+9) then + love.graphics.printf(challenge.name, 40, (260 - 20*(self.menu_state.challenge)) + 20 * idx, 200, "left") + cur_tagline = challenge.tagline + end + end + + + +end + +function ChallengeSelectScene:onInputPress(e) + if self.display_warning and e.input then + scene = TitleScene() + elseif e.type == "wheel" then + if e.x % 2 == 1 then + self:switchSelect() + end + if e.y ~= 0 then + self:changeOption(-e.y) + end + elseif e.input == "menu_decide" or e.scancode == "return" then + for idx, mode in pairs(game_modes) do + if mode.hash == challenges[self.menu_state.challenge].mode then + cur_mode = idx + break + end + end + for idx, ruleset in pairs(rulesets) do + if ruleset.hash == challenges[self.menu_state.challenge].ruleset then + cur_ruleset = idx + break + end + end + playSE("mode_decide") + saveConfig() + scene = ChallengeScene( + challenges[current_challenge], + rulesets[cur_ruleset] + ) + elseif e.input == "up" or e.scancode == "up" then + self:changeOption(-1) + self.das_up = true + self.das_down = nil + elseif e.input == "down" or e.scancode == "down" then + self:changeOption(1) + self.das_down = true + self.das_up = nil + elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then + scene = TitleScene() + elseif e.input then + self.secret_inputs[e.input] = true + end + + + love.graphics.printf("test???", 340, 258, 200, "left") + +end + +function ChallengeSelectScene:onInputRelease(e) + if e.input == "up" or e.scancode == "up" then + self.das_up = nil + elseif e.input == "down" or e.scancode == "down" then + self.das_down = nil + elseif e.input then + self.secret_inputs[e.input] = false + end +end + +function ChallengeSelectScene:changeOption(rel) + self:changechallenge(rel) + playSE("cursor") +end + + + +function ChallengeSelectScene:changechallenge(rel) + local len = table.getn(challenges) + self.menu_state.challenge = Mod1(self.menu_state.challenge + rel, len) +end + + + +return ChallengeSelectScene diff --git a/scene/mode_select.lua b/scene/mode_select.lua index d35594c..a7db60e 100755 --- a/scene/mode_select.lua +++ b/scene/mode_select.lua @@ -1,6 +1,6 @@ local ModeSelectScene = Scene:extend() -ModeSelectScene.title = "Game Start" +ModeSelectScene.title = "Freeplay" current_mode = 1 current_ruleset = 1 diff --git a/scene/title.lua b/scene/title.lua index 446a8d5..b5c22c0 100644 --- a/scene/title.lua +++ b/scene/title.lua @@ -5,6 +5,7 @@ TitleScene.restart_message = false local main_menu_screens = { ModeSelectScene, + ChallengeSelectScene, SettingsScene, CreditsScene, ExitScene, diff --git a/tetris/challenges/Tetrs.lua b/tetris/challenges/Tetrs.lua new file mode 100644 index 0000000..58f68e4 --- /dev/null +++ b/tetris/challenges/Tetrs.lua @@ -0,0 +1,26 @@ +require 'funcs' + +local GameMode = require 'tetris.modes.gamemode' +local Piece = require 'tetris.components.piece' +local Grid = require 'tetris.components.grid' +local Randomizer = require 'tetris.randomizers.randomizer' +local Bag7Randomizer = require 'tetris.randomizers.bag7noI' +local MarathonGF = require 'tetris.modes.marathon_gf' + +local TetrsChallenge = MarathonGF:extend() + +TetrsChallenge.name = "Tetrs" +TetrsChallenge.hash = "Tetrs" +TetrsChallenge.mode = "MarathonGF" +TetrsChallenge.ruleset = "Standard" +TetrsChallenge.tagline = "abababa" +TetrsChallenge.description = "Complete a mode with a specific ruleset and idk they did some other stupid things too lol" + +function TetrsChallenge:new() + + TetrsChallenge.super:new() + self.randomizer = Bag7Randomizer() + self.next_queue_length = 6 +end + +return TetrsChallenge diff --git a/tetris/challenges/challenge.lua b/tetris/challenges/challenge.lua new file mode 100644 index 0000000..7f777b1 --- /dev/null +++ b/tetris/challenges/challenge.lua @@ -0,0 +1,23 @@ +-- currently you need to require and extend the gamemode you're making a challenge out of + +require 'funcs' + +local GameMode = require 'tetris.modes.gamemode' +local Piece = require 'tetris.components.piece' +local Grid = require 'tetris.components.grid' +local Randomizer = require 'tetris.randomizers.randomizer' +local BagRandomizer = require 'tetris.randomizers.bag' +local MarathonGF = require 'tetris.modes.marathon_gf' + +local Challenge = GameMode:extend() + +Challenge.name = "A really cool challenge name" +Challenge.hash = "" +Challenge.mode = "" +Challenge.ruleset = "Guideline SRS" +Challenge.tagline = "Are you up for this challenge?" +Challenge.description = "Complete a mode with a specific ruleset and idk they did some other stupid things too lol" + + + +return Challenge diff --git a/tetris/modes/gamemode.lua b/tetris/modes/gamemode.lua index 2c805e3..2ae7b1d 100644 --- a/tetris/modes/gamemode.lua +++ b/tetris/modes/gamemode.lua @@ -143,7 +143,7 @@ function GameMode:update(inputs, ruleset) ) then self:onAttemptPieceRotate(self.piece, self.grid) end - + if self.piece == nil then self:processDelays(inputs, ruleset) else @@ -243,7 +243,7 @@ function GameMode:update(inputs, ruleset) if self.immobile_spin_bonus and self.piece.last_rotated and ( self.piece:isDropBlocked(self.grid) and - self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and + self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and self.piece:isMoveBlocked(self.grid, { x=0, y=-1 }) ) then @@ -251,7 +251,7 @@ function GameMode:update(inputs, ruleset) end self.grid:applyPiece(self.piece) - + -- mark squares (can be overridden) if self.square_mode then self.squares = self.squares + self.grid:markSquares() @@ -312,7 +312,7 @@ function GameMode:onAttemptPieceRotate(piece, grid) end function GameMode:onPieceMove(piece, grid, dx) end function GameMode:onPieceRotate(piece, grid, drot) end function GameMode:onPieceDrop(piece, grid, dy) end -function GameMode:onPieceLock(piece, cleared_row_count) +function GameMode:onPieceLock(piece, cleared_row_count) playSE("lock") end @@ -616,7 +616,7 @@ function GameMode:drawLineClearAnimation() -- animation function -- params: block x, y, skin, colour -- returns: table with RGBA, skin, colour, x, y - + -- Fadeout (default) --[[ function animation(x, y, skin, colour) @@ -728,8 +728,8 @@ function GameMode:drawNextQueue(ruleset) if self.hold_queue ~= nil and self.enable_hold then self:setHoldOpacity() drawPiece( - self.hold_queue.shape, - self.hold_queue.skin, + self.hold_queue.shape, + self.hold_queue.skin, ruleset.block_offsets[self.hold_queue.shape][self.hold_queue.orientation], -16, -32 ) @@ -828,7 +828,7 @@ end function GameMode:drawSectionTimesWithSplits(current_section, section_limit) section_limit = section_limit or math.huge - + local section_x = 440 local split_x = 530 @@ -843,7 +843,7 @@ function GameMode:drawSectionTimesWithSplits(current_section, section_limit) love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left") end end - + if (current_section <= section_limit) then love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left") love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left") diff --git a/tetris/randomizers/bag7noI.lua b/tetris/randomizers/bag7noI.lua new file mode 100644 index 0000000..922e867 --- /dev/null +++ b/tetris/randomizers/bag7noI.lua @@ -0,0 +1,19 @@ +-- for the pre-packaged/example challenge tetrs + +local Randomizer = require 'tetris.randomizers.randomizer' + +local Bag7NoIRandomizer = Randomizer:extend() + +function Bag7NoIRandomizer:initialize() + self.bag = {"J", "L", "O", "S", "T", "Z"} +end + +function Bag7NoIRandomizer:generatePiece() + if next(self.bag) == nil then + self.bag = {"J", "L", "O", "S", "T", "Z"} + end + local x = math.random(table.getn(self.bag)) + return table.remove(self.bag, x) +end + +return Bag7NoIRandomizer