diff --git a/funcs.lua b/funcs.lua index a61758d..ea195f6 100644 --- a/funcs.lua +++ b/funcs.lua @@ -61,4 +61,9 @@ function formatBigNum(number) if pos == 0 then pos = 3 end return string.sub(s, 1, pos) .. string.gsub(string.sub(s, pos+1), "(...)", ",%1") +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 end \ No newline at end of file diff --git a/discord-rpc.dll b/libs/discord-rpc.dll similarity index 100% rename from discord-rpc.dll rename to libs/discord-rpc.dll diff --git a/discordRPC.lua b/libs/discordRPC.lua similarity index 99% rename from discordRPC.lua rename to libs/discordRPC.lua index d6ba95a..c45c5da 100644 --- a/discordRPC.lua +++ b/libs/discordRPC.lua @@ -1,5 +1,5 @@ local ffi = require "ffi" -local discordRPClib = ffi.load("discord-rpc") +local discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc") ffi.cdef[[ typedef struct DiscordRichPresence { diff --git a/load/graphics.lua b/load/graphics.lua index 56f81aa..acce9b9 100644 --- a/load/graphics.lua +++ b/load/graphics.lua @@ -20,36 +20,54 @@ 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-gears.png") + input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"), + game_config = love.graphics.newImage("res/backgrounds/options-gears.png"), } blocks = { ["2tie"] = { - I = love.graphics.newImage("res/img/s1.png"), - J = love.graphics.newImage("res/img/s4.png"), - L = love.graphics.newImage("res/img/s3.png"), - O = love.graphics.newImage("res/img/s7.png"), - S = love.graphics.newImage("res/img/s5.png"), - T = love.graphics.newImage("res/img/s2.png"), - Z = love.graphics.newImage("res/img/s6.png"), - F = love.graphics.newImage("res/img/s9.png"), - G = love.graphics.newImage("res/img/s9.png"), + R = love.graphics.newImage("res/img/s1.png"), + O = love.graphics.newImage("res/img/s4.png"), + Y = love.graphics.newImage("res/img/s7.png"), + G = love.graphics.newImage("res/img/s6.png"), + C = love.graphics.newImage("res/img/s2.png"), + B = love.graphics.newImage("res/img/s3.png"), + M = love.graphics.newImage("res/img/s5.png"), X = love.graphics.newImage("res/img/s9.png"), }, ["bone"] = { - I = love.graphics.newImage("res/img/bone.png"), - J = love.graphics.newImage("res/img/bone.png"), - L = love.graphics.newImage("res/img/bone.png"), + R = love.graphics.newImage("res/img/bone.png"), O = love.graphics.newImage("res/img/bone.png"), - S = love.graphics.newImage("res/img/bone.png"), - T = love.graphics.newImage("res/img/bone.png"), - Z = love.graphics.newImage("res/img/bone.png"), - F = 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"), 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", + }, +} + for name, blockset in pairs(blocks) do for shape, image in pairs(blockset) do image:setFilter("nearest") diff --git a/main.lua b/main.lua index d74761c..aca1972 100644 --- a/main.lua +++ b/main.lua @@ -1,34 +1,33 @@ -discordRPC = require("discordRPC") -appId = "599778517789573120" - -function discordRPC.ready(userId, username, discriminator, avatar) - print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar)) -end - -function discordRPC.disconnected(errorCode, message) - print(string.format("Discord: disconnected (%d: %s)", errorCode, message)) -end - -function discordRPC.errored(errorCode, message) - print(string.format("Discord: error (%d: %s)", errorCode, message)) -end - -function discordRPC.joinGame(joinSecret) - print(string.format("Discord: join (%s)", joinSecret)) -end - -function discordRPC.spectateGame(spectateSecret) - print(string.format("Discord: spectate (%s)", spectateSecret)) -end - -function discordRPC.joinRequest(userId, username, discriminator, avatar) - print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar)) - discordRPC.respond(userId, "yes") -end - function love.load() + discordRPC = require("libs.discordRPC") + discordRPC.appId = "599778517789573120" - discordRPC.initialize(appId, true) + function discordRPC.ready(userId, username, discriminator, avatar) + print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar)) + end + + function discordRPC.disconnected(errorCode, message) + print(string.format("Discord: disconnected (%d: %s)", errorCode, message)) + end + + function discordRPC.errored(errorCode, message) + print(string.format("Discord: error (%d: %s)", errorCode, message)) + end + + function discordRPC.joinGame(joinSecret) + print(string.format("Discord: join (%s)", joinSecret)) + end + + function discordRPC.spectateGame(spectateSecret) + print(string.format("Discord: spectate (%s)", spectateSecret)) + end + + function discordRPC.joinRequest(userId, username, discriminator, avatar) + print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar)) + discordRPC.respond(userId, "yes") + end + + discordRPC.initialize(discordRPC.appId, true) local now = os.time(os.date("*t")) presence = { startTimestamp = now, @@ -59,6 +58,12 @@ function love.load() config.input = {} scene = InputConfigScene() else + 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 config.current_mode then current_mode = config.current_mode end if config.current_ruleset then current_ruleset = config.current_ruleset end scene = TitleScene() diff --git a/res/backgrounds/options-pcb.png b/res/backgrounds/options-pcb.png new file mode 100644 index 0000000..5d5d391 Binary files /dev/null and b/res/backgrounds/options-pcb.png differ diff --git a/scene.lua b/scene.lua index 14a67b2..f2b7a51 100644 --- a/scene.lua +++ b/scene.lua @@ -11,5 +11,5 @@ ExitScene = require "scene.exit" GameScene = require "scene.game" ModeSelectScene = require "scene.mode_select" InputConfigScene = require "scene.input_config" -ConfigScene = require "scene.config" +GameConfigScene = require "scene.game_config" TitleScene = require "scene.title" diff --git a/scene/game_config.lua b/scene/game_config.lua new file mode 100644 index 0000000..d71b38c --- /dev/null +++ b/scene/game_config.lua @@ -0,0 +1,78 @@ +local ConfigScene = Scene:extend() + +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 gamemode","Per ruleset","Harddrop", "Softdrop"}}, + {"piece_colour", "Piece Colours", {"Per ruleset", "Arika", "TTC"}}, + {"world_reverse", "World Reverse", {"No", "Yes"}}, +} +local optioncount = #ConfigScene.options + +function ConfigScene:new() + -- load current config + self.config = config.input + self.highlight = 1 + + presence.details = "In menus" + presence.state = "Changing game config" + discordRPC.updatePresence(presence) +end + +function ConfigScene:update() +end + +function ConfigScene:render() + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw( + backgrounds["game_config"], + 0, 0, 0, + 0.5, 0.5 + ) + + love.graphics.setFont(font_3x5_4) + 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.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 + end +end + +function ConfigScene:onKeyPress(e) + if e.scancode == "return" and e.isRepeat == false then + playSE("mode_decide") + saveConfig() + scene = TitleScene() + elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false 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 + playSE("cursor") + self.highlight = Mod1(self.highlight+1, optioncount) + elseif (e.scancode == config.input["left"] or e.scancode == "left") and e.isRepeat == false 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 + 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() + end +end + +return ConfigScene diff --git a/scene/input_config.lua b/scene/input_config.lua index 86dd305..9f6a9da 100644 --- a/scene/input_config.lua +++ b/scene/input_config.lua @@ -41,8 +41,8 @@ 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(input, 40, 50 + i * 20, 200, "left") love.graphics.printf( love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")", 240, 50 + i * 20, 200, "left" @@ -66,8 +66,13 @@ function ConfigScene:onKeyPress(e) self.input_state = 1 end else - config.input[configurable_inputs[self.input_state]] = e.scancode - self.input_state = self.input_state + 1 + 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/title.lua b/scene/title.lua index 60f282a..f8a314e 100644 --- a/scene/title.lua +++ b/scene/title.lua @@ -3,6 +3,7 @@ local TitleScene = Scene:extend() local main_menu_screens = { ModeSelectScene, InputConfigScene, + GameConfigScene, ExitScene, } diff --git a/tetris/components/grid.lua b/tetris/components/grid.lua index 1fbacde..68fab09 100644 --- a/tetris/components/grid.lua +++ b/tetris/components/grid.lua @@ -143,7 +143,7 @@ function Grid:copyBottomRow() for col = 1, 10 do self.grid[24][col] = (self.grid[23][col] == empty) and empty or { skin = self.grid[23][col].skin, - colour = "G" + colour = "X" } end return true @@ -161,7 +161,7 @@ function Grid:applyPiece(piece) if y + 1 > 0 then self.grid[y+1][x+1] = { skin = piece.skin, - colour = piece.shape + colour = piece.colour } end end @@ -177,7 +177,7 @@ function Grid:applyBigPiece(piece) if y*2+a > 0 then self.grid[y*2+a][x*2+b] = { skin = piece.skin, - colour = piece.shape + colour = piece.colour } end end diff --git a/tetris/components/piece.lua b/tetris/components/piece.lua index f45bdd4..15c0a76 100644 --- a/tetris/components/piece.lua +++ b/tetris/components/piece.lua @@ -2,7 +2,7 @@ local Object = require 'libs.classic' local Piece = Object:extend() -function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, big) +function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big) self.shape = shape self.rotation = rotation self.position = position @@ -10,6 +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.ghost = false self.locked = false self.big = big @@ -21,7 +22,7 @@ function Piece:withOffset(offset) return Piece( self.shape, self.rotation, {x = self.position.x + offset.x, y = self.position.y + offset.y}, - self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big + self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big ) end @@ -31,7 +32,7 @@ function Piece:withRelativeRotation(rot) while new_rot >= 4 do new_rot = new_rot - 4 end return Piece( self.shape, new_rot, self.position, - self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big + self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big ) end @@ -148,13 +149,13 @@ function Piece:draw(opacity, brightness, grid, partial_das) local y = self.position.y + offset.y if self.big then love.graphics.draw( - blocks[self.skin][self.shape], + blocks[self.skin][self.colour], 64+x*32+partial_das*2, 16+y*32+gravity_offset*2, 0, 2, 2 ) else love.graphics.draw( - blocks[self.skin][self.shape], + blocks[self.skin][self.colour], 64+x*16+partial_das, 16+y*16+gravity_offset ) end diff --git a/tetris/modes/gamemode.lua b/tetris/modes/gamemode.lua index 4cc3a30..c2ec87d 100644 --- a/tetris/modes/gamemode.lua +++ b/tetris/modes/gamemode.lua @@ -43,6 +43,8 @@ function GameMode:new() -- 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.hold_queue = nil self.held = false self.section_start_time = 0 @@ -59,6 +61,7 @@ function GameMode:getLineClearDelay() return 40 end function GameMode:getDasLimit() return 15 end function GameMode:getNextPiece(ruleset) + return { skin = "2tie", shape = self.randomizer:nextPiece(), @@ -72,6 +75,8 @@ function GameMode:initialize(ruleset) for i = 1, self.next_queue_length do table.insert(self.next_queue, self:getNextPiece(ruleset)) end + self.lock_on_soft_drop = ({self.instant_soft_drop, ruleset.softdrop_lock, false, true })[config.gamesettings.manlock] + self.lock_on_hard_drop = ({self.instant_hard_drop, ruleset.harddrop_lock, true, false})[config.gamesettings.manlock] end function GameMode:update(inputs, ruleset) @@ -125,7 +130,7 @@ function GameMode:update(inputs, ruleset) self.piece:isDropBlocked(self.grid) and not self.hard_drop_locked then self:onHardDrop(piece_dy) - if self.instant_hard_drop then + if self.lock_on_hard_drop then self.piece.locked = true end end @@ -134,7 +139,7 @@ function GameMode:update(inputs, ruleset) self:onSoftDrop(piece_dy) if self.piece:isDropBlocked(self.grid) and not self.drop_locked and - self.instant_soft_drop + self.lock_on_soft_drop then self.piece.locked = true end @@ -340,11 +345,12 @@ function GameMode:drawGhostPiece(ruleset) end function GameMode:drawNextQueue(ruleset) + 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 local y = offset.y + 4.7 - love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16) + love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16) end end for i = 1, self.next_queue_length do diff --git a/tetris/rulesets/arika.lua b/tetris/rulesets/arika.lua index 607d812..d21f039 100644 --- a/tetris/rulesets/arika.lua +++ b/tetris/rulesets/arika.lua @@ -110,7 +110,7 @@ function ARS:onPieceDrop(piece, grid) piece.lock_delay = 0 -- step reset end -function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end +function ARS:get180RotationValue() return 3 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 1bd379d..3c2801f 100755 --- a/tetris/rulesets/arika_ace.lua +++ b/tetris/rulesets/arika_ace.lua @@ -172,7 +172,7 @@ function ARS:onPieceRotate(piece, grid) end end -function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end +function ARS:get180RotationValue() return 3 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 c6ee6e1..0c99113 100755 --- a/tetris/rulesets/arika_srs.lua +++ b/tetris/rulesets/arika_srs.lua @@ -5,6 +5,18 @@ local SRS = Ruleset:extend() 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", +} +SRS.softdrop_lock = false +SRS.harddrop_lock = true SRS.enable_IRS_wallkicks = true diff --git a/tetris/rulesets/arika_ti.lua b/tetris/rulesets/arika_ti.lua index eaf51ac..da8652c 100644 --- a/tetris/rulesets/arika_ti.lua +++ b/tetris/rulesets/arika_ti.lua @@ -151,7 +151,7 @@ function ARS:onPieceDrop(piece, grid) piece.lock_delay = 0 -- step reset end -function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end +function ARS:get180RotationValue() return 3 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 148e5c1..6934304 100644 --- a/tetris/rulesets/cambridge.lua +++ b/tetris/rulesets/cambridge.lua @@ -5,6 +5,7 @@ local CRS = Ruleset:extend() CRS.name = "Cambridge" CRS.hash = "Cambridge" +CRS.world = true CRS.spawn_positions = { I = { x=5, y=4 }, @@ -362,6 +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 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 f5f7c89..0833116 100644 --- a/tetris/rulesets/ruleset.lua +++ b/tetris/rulesets/ruleset.lua @@ -6,6 +6,20 @@ local Ruleset = Object:extend() Ruleset.name = "" 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", +} +Ruleset.softdrop_lock = true +Ruleset.harddrop_lock = false + Ruleset.enable_IRS_wallkicks = false -- Component functions. @@ -39,6 +53,9 @@ 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 + rot_dir = 4 - rot_dir + end local new_piece = piece:withRelativeRotation(rot_dir) @@ -117,10 +134,12 @@ function Ruleset:initializePiece( else spawn_positions = self.spawn_positions end + 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 - }, self.block_offsets, 0, 0, data.skin, big) + }, self.block_offsets, 0, 0, data.skin, colours[data.shape], big) self:onPieceCreate(piece) self:rotatePiece(inputs, piece, grid, {}, true) diff --git a/tetris/rulesets/standard_exp.lua b/tetris/rulesets/standard_exp.lua index 82fa745..98fea77 100755 --- a/tetris/rulesets/standard_exp.lua +++ b/tetris/rulesets/standard_exp.lua @@ -5,6 +5,18 @@ local SRS = Ruleset:extend() 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", +} +SRS.softdrop_lock = false +SRS.harddrop_lock = true SRS.enable_IRS_wallkicks = true diff --git a/tetris/rulesets/ti_srs.lua b/tetris/rulesets/ti_srs.lua index 5acdcee..4caff19 100644 --- a/tetris/rulesets/ti_srs.lua +++ b/tetris/rulesets/ti_srs.lua @@ -5,6 +5,18 @@ local SRS = Ruleset:extend() 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", +} +SRS.softdrop_lock = false +SRS.harddrop_lock = true SRS.enable_IRS_wallkicks = true