Hopefully this is the folder support

it better be
This commit is contained in:
MarkGamed7794 2021-10-19 19:32:16 -04:00
parent d72f05d0de
commit 2384ceb3bf
19 changed files with 971 additions and 429 deletions

View File

@ -2,38 +2,46 @@ bgm = {
credit_roll = { credit_roll = {
gm3 = love.audio.newSource("res/bgm/tgm_credit_roll.mp3", "stream"), gm3 = love.audio.newSource("res/bgm/tgm_credit_roll.mp3", "stream"),
}, },
pacer_test = love.audio.newSource("res/bgm/pacer_test.mp3", "stream"), pacer_test = love.audio.newSource("res/bgm/pacer_test.mp3", "stream")
} }
local current_bgm = nil local current_bgm = nil
local bgm_locked = false local bgm_locked = false
local unfocused = false
function switchBGM(sound, subsound) function switchBGM(sound, subsound)
if bgm_locked then return end
if current_bgm ~= nil then if current_bgm ~= nil then
current_bgm:stop() current_bgm:stop()
end end
if bgm_locked or config.bgm_volume <= 0 then
current_bgm = nil
elseif sound ~= nil then
if subsound ~= nil then if subsound ~= nil then
current_bgm = bgm[sound][subsound] current_bgm = bgm[sound][subsound]
resetBGMFadeout() else
elseif sound ~= nil then
current_bgm = bgm[sound] current_bgm = bgm[sound]
resetBGMFadeout() end
else else
current_bgm = nil current_bgm = nil
end end
if current_bgm ~= nil then
resetBGMFadeout()
end
end end
function switchBGMLoop(sound, subsound) function switchBGMLoop(sound, subsound)
if bgm_locked then return end
switchBGM(sound, subsound) switchBGM(sound, subsound)
current_bgm:setLooping(true) if current_bgm then current_bgm:setLooping(true) end
end end
function lockBGM() function lockBGM()
bgm_locked = true bgm_locked = true
end end
function unlockBGM()
bgm_locked = false
end
local fading_bgm = false local fading_bgm = false
local fadeout_time = 0 local fadeout_time = 0
local total_fadeout_time = 0 local total_fadeout_time = 0
@ -49,11 +57,11 @@ end
function resetBGMFadeout(time) function resetBGMFadeout(time)
current_bgm:setVolume(config.bgm_volume) current_bgm:setVolume(config.bgm_volume)
fading_bgm = false fading_bgm = false
current_bgm:play() resumeBGM()
end end
function processBGMFadeout(dt) function processBGMFadeout(dt)
if fading_bgm then if current_bgm and fading_bgm then
fadeout_time = fadeout_time - dt fadeout_time = fadeout_time - dt
if fadeout_time < 0 then if fadeout_time < 0 then
fadeout_time = 0 fadeout_time = 0
@ -63,13 +71,20 @@ function processBGMFadeout(dt)
end end
end end
function pauseBGM() function pauseBGM(f)
if f then
unfocused = true
end
if current_bgm ~= nil then if current_bgm ~= nil then
current_bgm:pause() current_bgm:pause()
end end
end end
function resumeBGM() function resumeBGM(f)
if f and scene.paused and unfocused then
unfocused = false
return
end
if current_bgm ~= nil then if current_bgm ~= nil then
current_bgm:play() current_bgm:play()
end end

View File

@ -25,6 +25,15 @@ backgrounds = {
game_config = love.graphics.newImage("res/backgrounds/options-game.png"), game_config = love.graphics.newImage("res/backgrounds/options-game.png"),
} }
-- in order, the colors are:
-- red, orange, yellow, green, cyan, blue
-- magenta (or purple), white, black
-- the next three don't have colors tied to them
-- F is used for lock flash
-- A is a garbage block
-- X is an invisible "block"
-- don't use these for piece colors when making a ruleset
-- all the others are fine to use
blocks = { blocks = {
["2tie"] = { ["2tie"] = {
R = love.graphics.newImage("res/img/s1.png"), R = love.graphics.newImage("res/img/s1.png"),
@ -34,6 +43,8 @@ blocks = {
C = love.graphics.newImage("res/img/s2.png"), C = love.graphics.newImage("res/img/s2.png"),
B = love.graphics.newImage("res/img/s4.png"), B = love.graphics.newImage("res/img/s4.png"),
M = love.graphics.newImage("res/img/s5.png"), M = love.graphics.newImage("res/img/s5.png"),
W = love.graphics.newImage("res/img/s9.png"),
D = love.graphics.newImage("res/img/s8.png"),
F = love.graphics.newImage("res/img/s9.png"), F = love.graphics.newImage("res/img/s9.png"),
A = love.graphics.newImage("res/img/s8.png"), A = love.graphics.newImage("res/img/s8.png"),
X = love.graphics.newImage("res/img/s9.png"), X = love.graphics.newImage("res/img/s9.png"),
@ -46,6 +57,8 @@ blocks = {
C = love.graphics.newImage("res/img/bone.png"), C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"), B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"), M = love.graphics.newImage("res/img/bone.png"),
W = love.graphics.newImage("res/img/bone.png"),
D = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"), F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"), A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"), X = love.graphics.newImage("res/img/bone.png"),
@ -58,13 +71,16 @@ blocks = {
C = love.graphics.newImage("res/img/gem2.png"), C = love.graphics.newImage("res/img/gem2.png"),
B = love.graphics.newImage("res/img/gem4.png"), B = love.graphics.newImage("res/img/gem4.png"),
M = love.graphics.newImage("res/img/gem5.png"), M = love.graphics.newImage("res/img/gem5.png"),
W = love.graphics.newImage("res/img/gem9.png"),
D = love.graphics.newImage("res/img/gem9.png"),
F = love.graphics.newImage("res/img/gem9.png"), F = love.graphics.newImage("res/img/gem9.png"),
A = love.graphics.newImage("res/img/gem9.png"), A = love.graphics.newImage("res/img/gem9.png"),
X = love.graphics.newImage("res/img/gem9.png"), X = love.graphics.newImage("res/img/gem9.png"),
}, },
["square"] = { ["square"] = {
F = love.graphics.newImage("res/img/squares.png"), W = love.graphics.newImage("res/img/squares.png"),
Y = love.graphics.newImage("res/img/squareg.png"), Y = love.graphics.newImage("res/img/squareg.png"),
F = love.graphics.newImage("res/img/squares.png"),
X = love.graphics.newImage("res/img/squares.png"), X = love.graphics.newImage("res/img/squares.png"),
} }
} }
@ -87,7 +103,7 @@ ColourSchemes = {
Z = "R", Z = "R",
O = "Y", O = "Y",
T = "M", T = "M",
}, }
} }
for name, blockset in pairs(blocks) do for name, blockset in pairs(blocks) do

View File

@ -6,19 +6,53 @@ function loadSave()
end end
function loadFromFile(filename) function loadFromFile(filename)
local save_data, len = binser.readFile(filename) local file_data = love.filesystem.read(filename)
if file_data == nil then
return {} -- new object
end
local save_data = binser.deserialize(file_data)
if save_data == nil then if save_data == nil then
return {} -- new object return {} -- new object
end end
return save_data[1] return save_data[1]
end end
function initConfig()
if not config.das then config.das = 10 end
if not config.arr then config.arr = 2 end
if not config.dcd then config.dcd = 0 end
if not config.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end
if config.fullscreen == nil then config.fullscreen = false end
if config.secret == nil then config.secret = false 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
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
if not config.mode_path then config.mode_path = {} end
if not config.rule_path then config.rule_path = {} end
scene = TitleScene()
end
end
function saveConfig() function saveConfig()
binser.writeFile('config.sav', config) love.filesystem.write(
'config.sav', binser.serialize(config)
)
end end
function saveHighscores() function saveHighscores()
binser.writeFile('highscores.sav', highscores) love.filesystem.write(
'highscores.sav', binser.serialize(highscores)
)
end end

View File

@ -22,38 +22,48 @@ sounds = {
go = love.audio.newSource("res/se/go.wav", "static"), go = love.audio.newSource("res/se/go.wav", "static"),
irs = love.audio.newSource("res/se/irs.wav", "static"), irs = love.audio.newSource("res/se/irs.wav", "static"),
ihs = love.audio.newSource("res/se/ihs.wav", "static"), ihs = love.audio.newSource("res/se/ihs.wav", "static"),
clears = {
single = love.audio.newSource("res/se/single.wav", "static"),
double = love.audio.newSource("res/se/double.wav", "static"),
triple = love.audio.newSource("res/se/triple.wav", "static"),
quad = love.audio.newSource("res/se/quad.wav", "static")
},
-- a secret sound! -- a secret sound!
welcome = love.audio.newSource("res/se/welcomeToCambridge.wav", "static"), welcome = love.audio.newSource("res/se/welcomeToCambridge.wav", "static"),
} }
function playSE(sound, subsound) function playSE(sound, subsound)
if subsound == nil then if sound ~= nil then
sounds[sound]:setVolume(config.sfx_volume) if subsound ~= nil then
if sounds[sound]:isPlaying() then
sounds[sound]:stop()
end
sounds[sound]:play()
else
sounds[sound][subsound]:setVolume(config.sfx_volume) sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then if sounds[sound][subsound]:isPlaying() then
sounds[sound][subsound]:stop() sounds[sound][subsound]:stop()
end end
sounds[sound][subsound]:play() sounds[sound][subsound]:play()
else
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
sounds[sound]:stop()
end
sounds[sound]:play()
end
end end
end end
function playSEOnce(sound, subsound) function playSEOnce(sound, subsound)
if subsound == nil then if sound ~= nil then
sounds[sound]:setVolume(config.sfx_volume) if subsound ~= nil then
if sounds[sound]:isPlaying() then
return
end
sounds[sound]:play()
else
sounds[sound][subsound]:setVolume(config.sfx_volume) sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then if sounds[sound][subsound]:isPlaying() then
return return
end end
sounds[sound][subsound]:play() sounds[sound][subsound]:play()
else
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
return
end
sounds[sound]:play()
end
end end
end end

1
load/version.lua Normal file
View File

@ -0,0 +1 @@
version = "v0.3-beta7"

279
main.lua
View File

@ -8,106 +8,115 @@ function love.load()
require "load.bgm" require "load.bgm"
require "load.save" require "load.save"
require "load.bigint" require "load.bigint"
require "load.version"
loadSave() loadSave()
require "funcs"
require "scene" require "scene"
--config["side_next"] = false --config["side_next"] = false
--config["reverse_rotate"] = true --config["reverse_rotate"] = true
config["fullscreen"] = false --config["das_last_key"] = false
--config["fullscreen"] = false
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true}); love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
-- used for screenshots
GLOBAL_CANVAS = love.graphics.newCanvas()
-- init config -- init config
if not config.das then config.das = 10 end initConfig()
if not config.arr then config.arr = 2 end
if not config.dcd then config.dcd = 0 end
if not config.sfx_volume then config.sfx_volume = 0.5 end
if not config.bgm_volume then config.bgm_volume = 0.5 end
if config.secret == nil then config.secret = false love.window.setFullscreen(config["fullscreen"])
elseif config.secret == true then playSE("welcome") end if config.secret then playSE("welcome") end
if not config.gamesettings then -- import custom modules
config.gamesettings = {} initModules()
config["das_last_key"] = false end
function loadFiles(path,name)
local file = love.filesystem.getInfo(path)
if(file.type == "directory") then
local files_in = {}
local file_list = love.filesystem.getDirectoryItems(path)
--print("Encountered directory "..path)
table.insert(files_in,{type="back",name="(..)"})
for i,v in ipairs(file_list) do
if(v ~= "gamemode.lua") then table.insert(files_in, loadFiles(path.."/"..v,v)) end -- gamemode.lua is the base, not a mode
end
return {type="directory",name=name,content=files_in}
else else
config["das_last_key"] = config.gamesettings.das_last_key == 2 --print("Loaded file "..path)
end return {type="file",content=require(string.gsub(string.gsub(path,"/","."),"%.lua",""))}
for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1
end end
end end
if not config.input then function sort_folder_algo(a,b)
scene = InputConfigScene() local function padnum(d) return ("%03d%s"):format(#d, d) end
else if(a.type == "back" or b.type == "back") then
if config.current_mode then current_mode = config.current_mode end -- back always goes first
if config.current_ruleset then current_ruleset = config.current_ruleset end return a.type == "back"
scene = TitleScene() elseif(a.type == "directory" and b.type == "directory") then
-- if both are directories, sort by name
return tostring(a.name) < tostring(b.name)
elseif(a.type == "directory" or b.type == "directory") then
-- if one is a directory and the other's a file, then the directory always goes first
return a.type == "directory"
end
-- otherwise, they're both files, sort them by name
return tostring(a.content.name):gsub("%d+",padnum) < tostring(b.content.name):gsub("%d+",padnum)
end end
function sortFolder(folder)
-- step 1, sort the main level
table.sort(folder, sort_folder_algo)
-- step 2, recursively do it
for i,v in ipairs(folder) do
if(v.type == "directory") then
sortFolder(v.content)
end
end
end
function initModules()
game_modes = {} game_modes = {}
mode_list = love.filesystem.getDirectoryItems("tetris/modes") mode_list = love.filesystem.getDirectoryItems("tetris/modes")
for i,v in ipairs(mode_list) do
if(v ~= "gamemode.lua") then table.insert(game_modes, loadFiles("tetris/modes/"..v,v)) end
end
--[[
mode_list = love.filesystem.getDirectoryItems("tetris/modes")
for i=1,#mode_list do for i=1,#mode_list do
if(mode_list[i] ~= "gamemode.lua" and mode_list[i] ~= "unrefactored_modes") then
if(mode_list[i] ~= "gamemode.lua" and string.sub(mode_list[i], -4) == ".lua") then
game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5)) game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5))
end end
end end
--]]
rulesets = {} rulesets = {}
rule_list = love.filesystem.getDirectoryItems("tetris/rulesets") rule_list = love.filesystem.getDirectoryItems("tetris/rulesets")
for i,v in ipairs(rule_list) do
if(v ~= "ruleset.lua") then table.insert(rulesets, loadFiles("tetris/rulesets/"..v,v)) end
end
--[[
for i=1,#rule_list do for i=1,#rule_list do
if(rule_list[i] ~= "ruleset.lua" and rule_list[i] ~= "unrefactored_rulesets") then if(rule_list[i] ~= "ruleset.lua" and string.sub(rule_list[i], -4) == ".lua") then
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5)) rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
end end
end end
]]
--sort mode/rule lists --sort mode/rule lists
local function padnum(d) return ("%03d%s"):format(#d, d) end 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 sortFolder(game_modes)
sortFolder(rulesets)
local TARGET_FPS = 60
local SAMPLE_SIZE = 60
local rolling_samples = {}
local rolling_total = 0
local average_n = 0
local frame = 0
function getSmoothedDt(dt)
rolling_total = rolling_total + dt
frame = frame + 1
if frame > SAMPLE_SIZE then frame = frame - SAMPLE_SIZE end
if average_n == SAMPLE_SIZE then
rolling_total = rolling_total - rolling_samples[frame]
else
average_n = average_n + 1
end
rolling_samples[frame] = dt
return rolling_total / average_n
end
local update_time = 0.52
function love.update(dt)
processBGMFadeout(dt)
local old_update_time = update_time
update_time = update_time + getSmoothedDt(dt) * TARGET_FPS
updates = 0
while (update_time >= 1.02) do
scene:update()
updates = updates + 1
update_time = update_time - 1
end
if math.abs(update_time - old_update_time) < 0.02 then
update_time = old_update_time
end
end end
function love.draw() function love.draw()
love.graphics.setCanvas(GLOBAL_CANVAS)
love.graphics.clear()
love.graphics.push() love.graphics.push()
-- get offset matrix -- get offset matrix
@ -122,13 +131,23 @@ function love.draw()
love.graphics.scale(scale_factor) love.graphics.scale(scale_factor)
scene:render() scene:render()
love.graphics.setFont(font_3x5_2)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(version, 0, 460, 635, "right")
love.graphics.pop() love.graphics.pop()
love.graphics.setCanvas()
love.graphics.setColor(1,1,1,1)
love.graphics.draw(GLOBAL_CANVAS)
end end
function love.keypressed(key, scancode) function love.keypressed(key, scancode)
-- global hotkeys -- global hotkeys
if scancode == "f4" then if scancode == "f11" then
config["fullscreen"] = not config["fullscreen"] config["fullscreen"] = not config["fullscreen"]
saveConfig()
love.window.setFullscreen(config["fullscreen"]) love.window.setFullscreen(config["fullscreen"])
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
scene = InputConfigScene() scene = InputConfigScene()
@ -140,6 +159,16 @@ function love.keypressed(key, scancode)
scene.restart_message = true scene.restart_message = true
if config.secret then playSE("mode_decide") if config.secret then playSE("mode_decide")
else playSE("erase") end else playSE("erase") 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")
if not info then
love.filesystem.remove("ss")
love.filesystem.createDirectory("ss")
end
print("Saving screenshot as "..ss_name)
GLOBAL_CANVAS:newImageData():encode("png", ss_name)
-- function keys are reserved -- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return return
@ -210,13 +239,13 @@ function love.joystickaxis(joystick, axis, value)
config.input.joysticks[joystick:getName()].axes and config.input.joysticks[joystick:getName()].axes and
config.input.joysticks[joystick:getName()].axes[axis] config.input.joysticks[joystick:getName()].axes[axis]
then then
if math.abs(value) >= 0.5 then if math.abs(value) >= 1 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"] input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
end end
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
end end
if math.abs(value) >= 0.5 then if math.abs(value) >= 1 then
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value}) scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
else else
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value}) scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
@ -224,6 +253,14 @@ function love.joystickaxis(joystick, axis, value)
end end
end end
local last_hat_direction = ""
local directions = {
["u"] = "up",
["d"] = "down",
["l"] = "left",
["r"] = "right",
}
function love.joystickhat(joystick, hat, direction) function love.joystickhat(joystick, hat, direction)
local input_pressed = nil local input_pressed = nil
local has_hat = false local has_hat = false
@ -240,24 +277,116 @@ function love.joystickhat(joystick, hat, direction)
has_hat = true has_hat = true
end end
if input_pressed then if input_pressed then
scene:onInputPress({input=input_pressed, type="joyhat", name=joystick:getName(), hat=hat, direction=direction}) for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
scene:onInputPress({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
elseif has_hat then elseif has_hat then
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do 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}) scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end end
last_hat_direction = ""
elseif direction ~= "c" then elseif direction ~= "c" then
scene:onInputPress({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction}) for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
scene:onInputPress({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
scene:onInputRelease({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
else else
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do 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}) scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end end
last_hat_direction = ""
end end
end end
function love.wheelmoved(x, y)
scene:onInputPress({input=nil, type="wheel", x=x, y=y})
end
function love.focus(f) function love.focus(f)
if f and (scene.title ~= "Game" or not scene.paused) then if f then
resumeBGM() resumeBGM(true)
else else
pauseBGM() pauseBGM(true)
end
end
function love.resize(w, h)
GLOBAL_CANVAS:release()
GLOBAL_CANVAS = love.graphics.newCanvas(w, h)
end
local TARGET_FPS = 60
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
if love.timer then love.timer.step() end
local dt = 0
local last_time = love.timer.getTime()
local time_accumulator = 0
return function()
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
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
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
love.draw()
love.graphics.present()
end
local end_time = last_time + frame_duration
local time = love.timer.getTime()
while time < end_time do
love.timer.sleep(0.001)
time = love.timer.getTime()
end
time_accumulator = time_accumulator + time - last_time
end
time_accumulator = time_accumulator - frame_duration
end
last_time = love.timer.getTime()
end end
end end

View File

@ -11,6 +11,8 @@ function Scene:onInputRelease() end
ExitScene = require "scene.exit" ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"
ModeSelectScene = require "scene.mode_select" ModeSelectScene = require "scene.mode_select"
KeyConfigScene = require "scene.key_config"
StickConfigScene = require "scene.stick_config"
InputConfigScene = require "scene.input_config" InputConfigScene = require "scene.input_config"
GameConfigScene = require "scene.game_config" GameConfigScene = require "scene.game_config"
TuningScene = require "scene.tuning" TuningScene = require "scene.tuning"

View File

@ -4,23 +4,33 @@ CreditsScene.title = "Credits"
function CreditsScene:new() function CreditsScene:new()
self.frames = 0 self.frames = 0
-- higher = slower
self.scroll_speed = 1.85
switchBGM("credit_roll", "gm3") switchBGM("credit_roll", "gm3")
DiscordRPC:update({
details = "Watching the credits",
state = "Thanks for playing the game!",
largeImageKey = "ingame-1900",
})
end end
function CreditsScene:update() function CreditsScene:update()
if love.window.hasFocus() then if love.window.hasFocus() then
self.frames = self.frames + 1 self.frames = self.frames + 1
end end
if self.frames >= 4200 then if self.frames >= 2100 * self.scroll_speed then
playSE("mode_decide") playSE("mode_decide")
scene = TitleScene() scene = TitleScene()
switchBGM(nil) switchBGM(nil)
elseif self.frames == 3600 then elseif self.frames == math.floor(1950 * self.scroll_speed) then
fadeoutBGM(2) fadeoutBGM(2)
end end
end end
function CreditsScene:render() function CreditsScene:render()
local offset = self.frames / self.scroll_speed
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw( love.graphics.draw(
backgrounds[19], backgrounds[19],
@ -29,27 +39,43 @@ function CreditsScene:render()
) )
love.graphics.setFont(font_3x5_4) love.graphics.setFont(font_3x5_4)
love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2) love.graphics.print("Cambridge Credits", 320, 500 - offset)
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1500 - self.frames / 2, 240)) love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(2050 - offset, 240))
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.print("Game Developers", 320, 550 - self.frames / 2) love.graphics.print("Game Developers", 320, 550 - offset)
love.graphics.print("Project Heads", 320, 640 - self.frames / 2) love.graphics.print("Project Heads", 320, 640 - offset)
love.graphics.print("Other Game Developers", 320, 730 - self.frames / 2) love.graphics.print("Notable Game Developers", 320, 750 - offset)
love.graphics.print("Special Thanks", 320, 900 - self.frames / 2) love.graphics.print("Special Thanks", 320, 1020 - offset)
love.graphics.print("- SashLilac / SpinTriple", 320, math.max(2000 - self.frames / 2, 320)) love.graphics.print("- Milla", 320, math.max(2130 - offset, 320))
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - self.frames / 2) love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - offset)
love.graphics.print("Mizu\nHailey", 320, 680 - self.frames / 2) love.graphics.print("Mizu\nMarkGamed\nHailey", 320, 680 - offset)
love.graphics.print("Axel Fox - Multimino\nMine - Tetra Online\nDr Ocelot - Tetra Legends\nFelicity / nightmareci - Shiromino\n2Tie - TGMsim\nPhoenix Flare - Master of Blocks", 320, 770 - self.frames / 2)
love.graphics.print( love.graphics.print(
"RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" .. "2Tie - TGMsim\nAxel Fox - Multimino\nDr Ocelot - Tetra Legends\n" ..
"Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" .. "Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" ..
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\nMarkGamed7794\n" .. "Mine - Tetra Online\nMrZ - Techmino\nosk - TETR.IO\n" ..
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nThe Absolute PLUS Discord\nTetra Legends Discord\n" .. "Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" ..
"Tetra Online Discord\nMultimino Discord\nCambridge Discord\nAnd to you, the player!", "Rin - Puzzle Trial\nsinefuse - stackfuse",
320, 940 - self.frames / 2 320, 790 - offset
)
love.graphics.print(
"321MrHaatz\nAdventium\nAgentBasey\nArchina\nAurora\n" ..
"Caithness\nCheez\ncolour_thief\nCommando\nCublex\n" ..
"CylinderKnot\neightsixfivezero\nEricICX\nGesomaru\n" ..
"gizmo4487\nJBroms\nKirby703\nKitaru\n" ..
"M1ssing0\nMattMayuga\nMyPasswordIsWeak\n" ..
"Nikki Karissa\noffwo\nOliver\nPineapple\npokemonfan1937\n" ..
"Pyra Neoxi\nRDST64\nRocketLanterns\nRustyFoxxo\n" ..
"saphie\nShelleloch\nSimon\nstratus\nSuper302\n" ..
"switchpalacecorner\nterpyderp\nTetrian22\nTetro48\nThatCookie\n" ..
"TimmSkiller\nTrixciel\nuser74003\nZaptorZap\nZircean\n" ..
"All other contributors and friends!\nThe Absolute PLUS Discord\n" ..
"Tetra Legends Discord\nTetra Online Discord\nMultimino Discord\n" ..
"Hard Drop Discord\nRusty's Systemspace\nCambridge Discord\n" ..
"And to you, the player!",
320, 1060 - offset
) )
end end

View File

@ -7,10 +7,10 @@ require 'load.save'
function GameScene:new(game_mode, ruleset, inputs) function GameScene:new(game_mode, ruleset, inputs)
self.retry_mode = game_mode self.retry_mode = game_mode
self.retry_ruleset = ruleset self.retry_ruleset = ruleset
self.secret_inputs = copy(inputs) self.secret_inputs = inputs
self.game = game_mode(self.secret_inputs) self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset() self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset, self.secret_inputs) self.game:initialize(self.ruleset)
self.inputs = { self.inputs = {
left=false, left=false,
right=false, right=false,
@ -27,6 +27,7 @@ function GameScene:new(game_mode, ruleset, inputs)
DiscordRPC:update({ DiscordRPC:update({
details = self.game.rpc_details, details = self.game.rpc_details,
state = self.game.name, state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00"
}) })
end end
@ -38,94 +39,39 @@ function GameScene:update()
end end
self.game:update(inputs, self.ruleset) self.game:update(inputs, self.ruleset)
self.game.grid:update() self.game.grid:update()
DiscordRPC:update({
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end end
end end
function GameScene:render() function GameScene:render()
love.graphics.setColor(1, 1, 1, 1) self.game:draw(self.paused)
love.graphics.draw(
backgrounds[self.game:getBackground()],
0, 0, 0,
0.5, 0.5
)
-- game frame
if self.game.grid.width == 10 and self.game.grid.height == 24 then
love.graphics.draw(misc_graphics["frame"], 48, 64)
end
love.graphics.setColor(0, 0, 0, 200)
love.graphics.rectangle(
"fill", 64, 80,
16 * self.game.grid.width, 16 * (self.game.grid.height - 4)
)
if self.game.grid.width ~= 10 or self.game.grid.height ~= 24 then
love.graphics.setColor(174/255, 83/255, 76/255, 1)
love.graphics.setLineWidth(8)
love.graphics.line(
60,76,
68+16*self.game.grid.width,76,
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
60,84+16*(self.game.grid.height-4),
60,76
)
love.graphics.setColor(203/255, 137/255, 111/255, 1)
love.graphics.setLineWidth(4)
love.graphics.line(
60,76,
68+16*self.game.grid.width,76,
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
60,84+16*(self.game.grid.height-4),
60,76
)
love.graphics.setLineWidth(1)
end
self.game:drawGrid()
self.game:drawPiece()
self.game:drawNextQueue(self.ruleset)
self.game:drawScoringInfo()
-- ready/go graphics
if self.game.ready_frames <= 100 and self.game.ready_frames > 52 then
love.graphics.draw(misc_graphics["ready"], 144 - 50, 240 - 14)
elseif self.game.ready_frames <= 50 and self.game.ready_frames > 2 then
love.graphics.draw(misc_graphics["go"], 144 - 27, 240 - 14)
end
self.game:drawCustom()
love.graphics.setFont(font_3x5_2)
if config.gamesettings.display_gamemode == 1 then
love.graphics.printf(self.game.name .. " - " .. self.ruleset.name, 0, 460, 640, "left")
end
love.graphics.setFont(font_3x5_3)
if self.paused then love.graphics.print("PAUSED!", 80, 100) end
if self.game.completed then
self.game:onGameComplete()
elseif self.game.game_over then
self.game:onGameOver()
end
end end
function GameScene:onInputPress(e) function GameScene: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 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_entry = self.game:getHighscoreData()
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
submitHighscore(highscore_hash, highscore_entry) submitHighscore(highscore_hash, highscore_entry)
self.game:onExit()
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene() scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
elseif e.input == "retry" then elseif e.input == "retry" then
switchBGM(nil) switchBGM(nil)
self.game:onExit()
scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) 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 elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
self.paused = not self.paused self.paused = not self.paused
if self.paused then pauseBGM() if self.paused then pauseBGM()
else resumeBGM() end else resumeBGM() end
elseif e.input == "menu_back" then elseif e.input == "menu_back" then
self.game:onExit()
scene = ModeSelectScene() scene = ModeSelectScene()
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = true self.inputs[e.input] = true

View File

@ -11,13 +11,13 @@ ConfigScene.options = {
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}}, {"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}}, {"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
{"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}}, {"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
{"spawn_positions", "Spawn Positions", false, {"In field", "Out of field"}}, {"spawn_positions", "Spawn Positions", false, {"Per ruleset", "In field", "Out of field"}},
{"display_gamemode", "Display Gamemode", false, {"On", "Off"}}, {"display_gamemode", "Display Gamemode", false, {"On", "Off"}},
{"das_last_key", "DAS Switch", false, {"Default", "Instant"}}, {"das_last_key", "DAS Last Key", false, {"Off", "On"}},
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}}, {"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
{"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}}, {"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}},
{"diagonal_input", "Diagonal Input", false, {"On", "Off"}}, {"diagonal_input", "Diagonal Input", false, {"On", "Off"}},
{"buffer_lock", "Buffer Lock Inputs", false, {"On", "Off"}}, {"buffer_lock", "Buffer Drop Type", false, {"Off", "Hold", "Tap"}},
{"sfx_volume", "SFX", true, "sfxSlider"}, {"sfx_volume", "SFX", true, "sfxSlider"},
{"bgm_volume", "BGM", true, "bgmSlider"}, {"bgm_volume", "BGM", true, "bgmSlider"},
} }
@ -29,7 +29,7 @@ function ConfigScene:new()
self.highlight = 1 self.highlight = 1
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In settings",
state = "Changing game settings", state = "Changing game settings",
}) })
@ -38,7 +38,6 @@ function ConfigScene:new()
end end
function ConfigScene:update() function ConfigScene:update()
config["das_last_key"] = config.gamesettings.das_last_key == 2
self.sfxSlider:update() self.sfxSlider:update()
self.bgmSlider:update() self.bgmSlider:update()
end end
@ -101,9 +100,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight] local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[4]) config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[4])
else else
local sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 5) / (sld.max - sld.min)))
sld:update()
playSE("cursor") playSE("cursor")
sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 3) / (sld.max - sld.min)))
end end
elseif e.input == "right" or e.scancode == "right" then elseif e.input == "right" or e.scancode == "right" then
if not self.options[self.highlight][3] then if not self.options[self.highlight][3] then
@ -111,9 +111,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight] local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[4]) config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[4])
else else
playSE("cursor")
sld = self[self.options[self.highlight][4]] sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 3) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min)) sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))
sld:update()
playSE("cursor")
end end
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave() loadSave()

View File

@ -2,47 +2,21 @@ local ConfigScene = Scene:extend()
ConfigScene.title = "Input Config" ConfigScene.title = "Input Config"
require 'load.save' local menu_screens = {
KeyConfigScene,
local configurable_inputs = { StickConfigScene
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
} }
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function ConfigScene:new() function ConfigScene:new()
self.input_state = 1 self.menu_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
self.axis_timer = 0
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In settings",
state = "Changing input config", state = "Changing input config",
largeImageKey = "settings-input"
}) })
end end
function ConfigScene:update() function ConfigScene:update() end
end
function ConfigScene:render() function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@ -52,111 +26,41 @@ function ConfigScene:render()
0.5, 0.5 0.5, 0.5
) )
love.graphics.setFont(font_3x5_4)
love.graphics.print("INPUT CONFIG", 80, 40)
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for i, input in ipairs(configurable_inputs) do love.graphics.print("Which controls do you want to configure?", 80, 90)
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if self.set_inputs[input] then love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.printf(self.set_inputs[input], 240, 50 + i * 20, 300, "left") love.graphics.rectangle("fill", 75, 118 + 50 * self.menu_state, 200, 33)
love.graphics.setFont(font_3x5_3)
love.graphics.setColor(1, 1, 1, 1)
for i, screen in pairs(menu_screens) do
love.graphics.printf(screen.title, 80, 120 + 50 * i, 200, "left")
end end
end end
if self.input_state > table.getn(configurable_inputs) then
love.graphics.print("press enter to confirm, delete/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
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
self.axis_timer = self.axis_timer + 1 function ConfigScene:changeOption(rel)
end local len = table.getn(menu_screens)
self.menu_state = (self.menu_state + len + rel - 1) % len + 1
local function addJoystick(input, name)
if not input.joysticks then
input.joysticks = {}
end
if not input.joysticks[name] then
input.joysticks[name] = {}
end
end end
function ConfigScene:onInputPress(e) function ConfigScene:onInputPress(e)
if e.type == "key" then if e.input == "menu_decide" or e.scancode == "return" then
-- function keys, escape, and tab are reserved and can't be remapped playSE("main_decide")
if e.scancode == "escape" and config.input then scene = menu_screens[self.menu_state]()
-- cancel only if there was an input config already elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1)
playSE("cursor")
elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1)
playSE("cursor")
elseif config.input and (
e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete"
) then
scene = SettingsScene() scene = SettingsScene()
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 (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 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 >= 1 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 >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.last_axis = e.axis
self.axis_timer = 0
end
elseif e.type == "joyhat" then
if e.direction ~= "c" then
addJoystick(self.new_input, e.name)
if not self.new_input.joysticks[e.name].hats then
self.new_input.joysticks[e.name].hats = {}
end
if not self.new_input.joysticks[e.name].hats[e.hat] then
self.new_input.joysticks[e.name].hats[e.hat] = {}
end
self.set_inputs[configurable_inputs[self.input_state]] =
"jhat " ..
e.hat .. " " .. e.direction ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input.joysticks[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
end end
end end

100
scene/key_config.lua Normal file
View File

@ -0,0 +1,100 @@
local KeyConfigScene = Scene:extend()
KeyConfigScene.title = "Key Config"
require 'load.save'
local configurable_inputs = {
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
}
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function KeyConfigScene:new()
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
DiscordRPC:update({
details = "In settings",
state = "Changing key config",
})
end
function KeyConfigScene:update()
end
function KeyConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["input_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setFont(font_3x5_2)
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/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press key input for " .. configurable_inputs[self.input_state] .. ", tab to skip, escape to cancel", 0, 0)
love.graphics.print("function keys (F1, F2, etc.), escape, and tab can't be changed", 0, 20)
end
end
function KeyConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" then
scene = InputConfigScene()
elseif self.input_state > table.getn(configurable_inputs) then
if e.scancode == "return" then
-- save new input, then load next scene
local had_config = config.input ~= nil
if not config.input then config.input = {} end
config.input.keys = self.new_input
saveConfig()
scene = had_config and InputConfigScene() or 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" and not self.new_input[e.scancode] then
-- all other keys can be configured
self.set_inputs[configurable_inputs[self.input_state]] = "key " .. love.keyboard.getKeyFromScancode(e.scancode) .. " (" .. e.scancode .. ")"
self.new_input[e.scancode] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
return KeyConfigScene

View File

@ -6,27 +6,113 @@ current_mode = 1
current_ruleset = 1 current_ruleset = 1
function ModeSelectScene:new() function ModeSelectScene:new()
-- reload custom modules
initModules()
if table.getn(game_modes) == 0 or table.getn(rulesets) == 0 then
self.display_warning = true
current_mode = 1
current_ruleset = 1
else
self.display_warning = false
if current_mode > table.getn(game_modes) then
current_mode = 1
end
if current_ruleset > table.getn(rulesets) then
current_ruleset = 1
end
end
self.menu_state = { self.menu_state = {
mode = current_mode, mode = current_mode,
mode_list = game_modes,
mode_list_history = {},
mode_path = (config.mode_path or {}),
ruleset = current_ruleset, ruleset = current_ruleset,
rule_list = rulesets,
rule_list_history = {},
rule_path = config.rule_path,
select = "mode", select = "mode",
} }
self.secret_inputs = { self.secret_inputs = {}
rotate_left = false, self.das = 0
rotate_left2 = false,
rotate_right = false,
rotate_right2 = false,
rotate_180 = false,
hold = false,
}
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = "Choosing a mode", state = "Choosing a mode",
largeImageKey = "ingame-000"
}) })
-- grab the filepaths from config to ensure you're dropped off on the correct mode and ruleset
local able_to_find = true
for _,looking_for in pairs(self.menu_state.mode_path) do
if(able_to_find) then
local found = false
for idx,file in pairs(self.menu_state.mode_list) do
if(not found) then
if(file.type == "directory" and file.name == looking_for) then
table.insert(self.menu_state.mode_list_history,self.menu_state.mode_list)
self.menu_state.mode_list = self.menu_state.mode_list[idx].content
found = true
end
end
end
able_to_find = (found and able_to_find)
end
end
if(not able_to_find) then
--directory doesn't exist, revert to default
self.menu_state.mode_list = game_modes
self.menu_state.mode_list_history = {}
self.menu_state.mode_path = {}
self.menu_state.mode = 1
current_mode = 1
end
-- do the same with the ruleset
local able_to_find = true
for _,looking_for in pairs(self.menu_state.rule_path) do
if(able_to_find) then
local found = false
for idx,file in pairs(self.menu_state.rule_list) do
if(not found) then
if(file.type == "directory" and file.name == looking_for) then
table.insert(self.menu_state.rule_list_history,self.menu_state.rule_list)
self.menu_state.rule_list = self.menu_state.rule_list[idx].content
found = true
end
end
end
able_to_find = (found and able_to_find)
end
end
if(not able_to_find) then
--directory doesn't exist, revert to default
self.menu_state.rule_list = game_modes
self.menu_state.rule_list_history = {}
self.menu_state.rule_path = {}
self.menu_state.rule = 1
current_mode = 1
end
end end
function ModeSelectScene:update() function ModeSelectScene:update()
switchBGM(nil) -- experimental 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 " .. self.menu_state.select,
largeImageKey = "ingame-000"
})
end end
function ModeSelectScene:render() function ModeSelectScene:render()
@ -36,6 +122,23 @@ function ModeSelectScene:render()
0.5, 0.5 0.5, 0.5
) )
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
if self.display_warning then
love.graphics.setFont(font_3x5_3)
love.graphics.printf(
"You have no modes or rulesets.",
80, 200, 480, "center"
)
love.graphics.setFont(font_3x5_2)
love.graphics.printf(
"Come back to this menu after getting more modes or rulesets. " ..
"Press any button to return to the main menu.",
80, 250, 480, "center"
)
return
end
if self.menu_state.select == "mode" then if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
@ -52,39 +155,87 @@ function ModeSelectScene:render()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do for idx, mode in pairs(self.menu_state.mode_list) do
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then 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") if(mode.type == "directory") then
love.graphics.setColor(1,0.5,0,1)
elseif(mode.type == "back") then
love.graphics.setColor(1,1,0,1)
else
love.graphics.setColor(1,1,1,1)
end
love.graphics.printf((mode.type == "file" and mode.content.name or mode.name), 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
end end
end end
for idx, ruleset in pairs(rulesets) do for idx, ruleset in pairs(self.menu_state.rule_list) do
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then 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") if(ruleset.type == "directory") then
love.graphics.setColor(1,0.5,0,1)
elseif(ruleset.type == "back") then
love.graphics.setColor(1,1,0,1)
else
love.graphics.setColor(1,1,1,1)
end
love.graphics.printf((ruleset.type == "file" and ruleset.content.name or ruleset.name), 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 200, "left")
end end
end end
end end
function ModeSelectScene:onInputPress(e) function ModeSelectScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" then 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
if self.menu_state.mode_list[self.menu_state.mode].type == "directory" and self.menu_state.select == "mode" then
table.insert(self.menu_state.mode_list_history,self.menu_state.mode_list)
table.insert(self.menu_state.mode_path,self.menu_state.mode_list[self.menu_state.mode].name)
self.menu_state.mode_list = self.menu_state.mode_list[self.menu_state.mode].content
self.menu_state.mode = 1
elseif self.menu_state.mode_list[self.menu_state.mode].type == "back" and self.menu_state.select == "mode" then
table.remove(self.menu_state.mode_path)
self.menu_state.mode_list = table.remove(self.menu_state.mode_list_history)
self.menu_state.mode = 1
elseif self.menu_state.rule_list[self.menu_state.ruleset].type == "directory" and self.menu_state.select == "ruleset" then
table.insert(self.menu_state.rule_list_history,self.menu_state.rule_list)
table.insert(self.menu_state.rule_path,self.menu_state.rule_list[self.menu_state.ruleset].name)
self.menu_state.rule_list = self.menu_state.rule_list[self.menu_state.ruleset].content
self.menu_state.ruleset = 1
elseif self.menu_state.rule_list[self.menu_state.ruleset].type == "back" and self.menu_state.select == "ruleset" then
table.remove(self.menu_state.rule_path)
self.menu_state.rule_list = table.remove(self.menu_state.rule_list_history)
self.menu_state.ruleset = 1
elseif(self.menu_state.mode_list[self.menu_state.mode].type == "file" and self.menu_state.rule_list[self.menu_state.ruleset].type == "file") then -- make sure neither option is on a folder
current_mode = self.menu_state.mode current_mode = self.menu_state.mode
current_ruleset = self.menu_state.ruleset current_ruleset = self.menu_state.ruleset
config.current_mode = current_mode config.current_mode = current_mode
config.mode_path = self.menu_state.mode_path
config.current_ruleset = current_ruleset config.current_ruleset = current_ruleset
config.rule_path = self.menu_state.rule_path
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs) scene = GameScene(
self.menu_state.mode_list[self.menu_state.mode].content,
self.menu_state.rule_list[self.menu_state.ruleset].content,
self.secret_inputs
)
end
elseif e.input == "up" or e.scancode == "up" then elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1) self:changeOption(-1)
playSE("cursor") self.das_up = true
self.das_down = nil
elseif e.input == "down" or e.scancode == "down" then elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1) self:changeOption(1)
playSE("cursor") self.das_down = true
self.das_up = nil
elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then
self:switchSelect() self:switchSelect()
playSE("cursor_lr")
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene() scene = TitleScene()
elseif e.input then elseif e.input then
@ -93,7 +244,11 @@ function ModeSelectScene:onInputPress(e)
end end
function ModeSelectScene:onInputRelease(e) function ModeSelectScene:onInputRelease(e)
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then 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 self.secret_inputs[e.input] = false
end end
end end
@ -104,24 +259,26 @@ function ModeSelectScene:changeOption(rel)
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
self:changeRuleset(rel) self:changeRuleset(rel)
end end
playSE("cursor")
end end
function ModeSelectScene:switchSelect(rel) function ModeSelectScene:switchSelect()
if self.menu_state.select == "mode" then if self.menu_state.select == "mode" then
self.menu_state.select = "ruleset" self.menu_state.select = "ruleset"
elseif self.menu_state.select == "ruleset" then elseif self.menu_state.select == "ruleset" then
self.menu_state.select = "mode" self.menu_state.select = "mode"
end end
playSE("cursor_lr")
end end
function ModeSelectScene:changeMode(rel) function ModeSelectScene:changeMode(rel)
local len = table.getn(game_modes) local len = table.getn(self.menu_state.mode_list)
self.menu_state.mode = (self.menu_state.mode + len + rel - 1) % len + 1 self.menu_state.mode = Mod1(self.menu_state.mode + rel, len)
end end
function ModeSelectScene:changeRuleset(rel) function ModeSelectScene:changeRuleset(rel)
local len = table.getn(rulesets) local len = table.getn(self.menu_state.rule_list)
self.menu_state.ruleset = (self.menu_state.ruleset + len + rel - 1) % len + 1 self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len)
end end
return ModeSelectScene return ModeSelectScene

View File

@ -8,11 +8,20 @@ local menu_screens = {
TuningScene TuningScene
} }
local settingsidle = {
"Tweaking some knobs",
"Tuning up",
"Adjusting options",
"Setting up",
"Setting the settings"
}
function SettingsScene:new() function SettingsScene:new()
self.menu_state = 1 self.menu_state = 1
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In settings",
state = "Changing settings", state = settingsidle[math.random(#settingsidle)],
largeImageKey = "settings",
}) })
end end

159
scene/stick_config.lua Normal file
View File

@ -0,0 +1,159 @@
local StickConfigScene = Scene:extend()
StickConfigScene.title = "Joystick Config"
require 'load.save'
local configurable_inputs = {
"menu_decide",
"menu_back",
"left",
"right",
"up",
"down",
"rotate_left",
"rotate_left2",
"rotate_right",
"rotate_right2",
"rotate_180",
"hold",
"retry",
"pause",
}
local function newSetInputs()
local set_inputs = {}
for i, input in ipairs(configurable_inputs) do
set_inputs[input] = false
end
return set_inputs
end
function StickConfigScene:new()
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
self.axis_timer = 0
DiscordRPC:update({
details = "In settings",
state = "Changing joystick config",
})
end
function StickConfigScene:update()
end
function StickConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["input_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setFont(font_3x5_2)
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/backspace to retry" .. (config.input and ", escape to cancel" or ""))
else
love.graphics.print("press joystick input for " .. configurable_inputs[self.input_state] .. ", tab to skip, escape to cancel", 0, 0)
end
self.axis_timer = self.axis_timer + 1
end
local function addJoystick(input, name)
if not input[name] then
input[name] = {}
end
end
function StickConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" then
scene = InputConfigScene()
elseif self.input_state > table.getn(configurable_inputs) then
if e.scancode == "return" then
-- save new input, then load next scene
local had_config = config.input ~= nil
if not config.input then config.input = {} end
config.input.joysticks = self.new_input
saveConfig()
scene = had_config and InputConfigScene() or 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
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[e.name].buttons then
self.new_input[e.name].buttons = {}
end
if self.new_input[e.name].buttons[e.button] then return 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[e.name].buttons[e.button] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
elseif e.type == "joyaxis" then
if (e.axis ~= self.last_axis or self.axis_timer > 30) and math.abs(e.value) >= 1 then
addJoystick(self.new_input, e.name)
if not self.new_input[e.name].axes then
self.new_input[e.name].axes = {}
end
if not self.new_input[e.name].axes[e.axis] then
self.new_input[e.name].axes[e.axis] = {}
end
if (
self.new_input[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"]
) then return end
self.set_inputs[configurable_inputs[self.input_state]] =
"jaxis " ..
(e.value >= 1 and "+" or "-") .. e.axis ..
" " .. string.sub(e.name, 1, 10) .. (string.len(e.name) > 10 and "..." or "")
self.new_input[e.name].axes[e.axis][e.value >= 1 and "positive" or "negative"] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
self.last_axis = e.axis
self.axis_timer = 0
end
elseif e.type == "joyhat" then
if e.direction ~= "c" then
addJoystick(self.new_input, e.name)
if not self.new_input[e.name].hats then
self.new_input[e.name].hats = {}
end
if not self.new_input[e.name].hats[e.hat] then
self.new_input[e.name].hats[e.hat] = {}
end
if self.new_input[e.name].hats[e.hat][e.direction] then
return
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[e.name].hats[e.hat][e.direction] = configurable_inputs[self.input_state]
self.input_state = self.input_state + 1
end
end
end
end
end
return StickConfigScene

View File

@ -23,6 +23,15 @@ local mainmenuidle = {
"Having a nap", "Having a nap",
"In menus", "In menus",
"Bottom text", "Bottom text",
"Trying to see all the funny rpc messages (maybe)",
"Not not not playing",
"AFK",
"Preparing for their next game",
"Who are those people on that boat?",
"Welcome to Cambridge!",
"who even reads these",
"Made with love in LOVE!",
"This is probably the longest RPC string out of every possible RPC string that can be displayed."
} }
function TitleScene:new() function TitleScene:new()
@ -35,6 +44,8 @@ function TitleScene:new()
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = mainmenuidle[math.random(#mainmenuidle)], state = mainmenuidle[math.random(#mainmenuidle)],
largeImageKey = "icon2",
largeImageText = version
}) })
end end
@ -82,7 +93,6 @@ function TitleScene:render()
for i, screen in pairs(main_menu_screens) do for i, screen in pairs(main_menu_screens) do
love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left") love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left")
end end
end end
function TitleScene:changeOption(rel) function TitleScene:changeOption(rel)
@ -106,6 +116,9 @@ function TitleScene:onInputPress(e)
self.text = self.text .. (e.scancode ~= nil and e.scancode or "") self.text = self.text .. (e.scancode ~= nil and e.scancode or "")
if self.text == "ffffff" then if self.text == "ffffff" then
self.text_flag = true self.text_flag = true
DiscordRPC:update({
largeImageKey = "snow"
})
end end
end end
end end

View File

@ -16,7 +16,7 @@ local optioncount = #TuningScene.options
function TuningScene:new() function TuningScene:new()
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In settings",
state = "Changing tuning settings", state = "Changing tuning settings",
}) })
self.highlight = 1 self.highlight = 1

View File

@ -111,18 +111,24 @@ function Grid:getClearedRowCount()
end end
function Grid:markClearedRows() function Grid:markClearedRows()
local block_table = {}
for row = 1, self.height do for row = 1, self.height do
if self:isRowFull(row) then if self:isRowFull(row) then
block_table[row] = {}
for x = 1, self.width do for x = 1, self.width do
block_table[row][x] = {
skin = self.grid[row][x].skin,
colour = self.grid[row][x].colour,
}
self.grid[row][x] = { self.grid[row][x] = {
skin = self.grid[row][x].skin, skin = self.grid[row][x].skin,
colour = "X" colour = "X"
} }
self.grid_age[row][x] = 0 --self.grid_age[row][x] = 0
end end
end end
end end
return true return block_table
end end
function Grid:clearClearedRows() function Grid:clearClearedRows()
@ -201,7 +207,7 @@ function Grid:applyBigPiece(piece)
y = piece.position.y + offset.y y = piece.position.y + offset.y
for a = 1, 2 do for a = 1, 2 do
for b = 1, 2 do for b = 1, 2 do
if y*2+a > 0 then if y*2+a > 0 and y*2 < self.height then
self.grid[y*2+a][x*2+b] = { self.grid[y*2+a][x*2+b] = {
skin = piece.skin, skin = piece.skin,
colour = piece.colour colour = piece.colour
@ -343,7 +349,7 @@ function Grid:markSquares()
elseif i == 2 then elseif i == 2 then
for j = 0, 3 do for j = 0, 3 do
for k = 0, 3 do for k = 0, 3 do
self.grid[y+j][x+k].colour = "F" self.grid[y+j][x+k].colour = "W"
self.grid[y+j][x+k].skin = "square" self.grid[y+j][x+k].skin = "square"
end end
@ -388,15 +394,16 @@ end
function Grid:draw() function Grid:draw()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if blocks[self.grid[y][x].skin] and
blocks[self.grid[y][x].skin][self.grid[y][x].colour] then
if self.grid_age[y][x] < 2 then if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
else else
if self.grid[y][x].skin == "bone" then if self.grid[y][x].colour == "X" then
love.graphics.setColor(0, 0, 0, 0)
elseif self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
elseif self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
else else
love.graphics.setColor(0.5, 0.5, 0.5, 1) love.graphics.setColor(0.5, 0.5, 0.5, 1)
end end
@ -405,11 +412,11 @@ function Grid:draw()
if self.grid[y][x].skin ~= "bone" and self.grid[y][x].colour ~= "X" then if self.grid[y][x].skin ~= "bone" and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1) love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@ -427,18 +434,14 @@ end
function Grid:drawOutline() function Grid:drawOutline()
for y = 5, self.height do for y = 5, self.height do
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x].colour == "X" then
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
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] ~= empty and self.grid[y][x].colour ~= "X" then if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1) love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@ -459,7 +462,7 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
for x = 1, self.width do for x = 1, self.width do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
opacity = 1 - self.grid_age[y][x] / 15 opacity = 0
elseif garbage_opacity_function and self.grid[y][x].colour == "A" then elseif garbage_opacity_function and self.grid[y][x].colour == "A" then
opacity = garbage_opacity_function(self.grid_age[y][x]) opacity = garbage_opacity_function(self.grid_age[y][x])
else else
@ -471,11 +474,11 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
if opacity > 0 and self.grid[y][x].colour ~= "X" then if opacity > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64) love.graphics.setColor(0.64, 0.64, 0.64)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then
@ -506,18 +509,18 @@ function Grid:drawCustom(colour_function, gamestate)
if block ~= empty then if block ~= empty then
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x]) local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
if self.grid[y][x].colour == "X" then if self.grid[y][x].colour == "X" then
A = 1 - self.grid_age[y][x] / 15 A = 0
end end
love.graphics.setColor(R, G, B, A) love.graphics.setColor(R, G, B, A)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
if outline > 0 and self.grid[y][x].colour ~= "X" then if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline) love.graphics.setColor(0.64, 0.64, 0.64, outline)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16) love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end end
if y < self.height and self.grid[y+1][x] == empty or if y < self.height and self.grid[y+1][x] == empty or
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then (y + 1 <= self.height and self.grid[y+1][x].colour == "X") then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16) love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end end
if x > 1 and self.grid[y][x-1] == empty then if x > 1 and self.grid[y][x-1] == empty then

View File

@ -104,9 +104,8 @@ function Piece:dropToBottom(grid)
self:dropSquares(math.huge, grid) self:dropSquares(math.huge, grid)
self.gravity = 0 self.gravity = 0
if self.position.y > piece_y then if self.position.y > piece_y then
-- if it got dropped any, also reset lock delay
if self.ghost == false then playSE("bottom") end if self.ghost == false then playSE("bottom") end
self.lock_delay = 0 -- self.lock_delay = 0
end end
return self return self
end end
@ -118,19 +117,37 @@ function Piece:lockIfBottomed(grid)
return self return self
end end
function Piece:addGravity(gravity, grid) function Piece:addGravity(gravity, grid, classic_lock)
gravity = gravity / (self.big and 2 or 1)
local new_gravity = self.gravity + gravity local new_gravity = self.gravity + gravity
if self:isDropBlocked(grid) then if self:isDropBlocked(grid) then
self.gravity = math.min(1, new_gravity) if classic_lock then
self.lock_delay = self.lock_delay + 1 self.gravity = new_gravity
else else
local dropped_squares = math.floor(new_gravity) self.gravity = 0
self.lock_delay = self.lock_delay + 1
end
elseif not (
self:isMoveBlocked(grid, { x=0, y=-1 }) and gravity < 0
) then
local dropped_squares = math.floor(math.abs(new_gravity))
if gravity >= 0 then
local new_frac_gravity = new_gravity - dropped_squares local new_frac_gravity = new_gravity - dropped_squares
self.gravity = new_frac_gravity self.gravity = new_frac_gravity
self:dropSquares(dropped_squares, grid) self:dropSquares(dropped_squares, grid)
if self:isDropBlocked(grid) then if self:isDropBlocked(grid) then
playSE("bottom") playSE("bottom")
end end
else
local new_frac_gravity = new_gravity + dropped_squares
self.gravity = new_frac_gravity
self:moveInGrid({ x=0, y=-1 }, dropped_squares, grid)
if self:isMoveBlocked(grid, { x=0, y=-1 }) then
playSE("bottom")
end
end
else
self.gravity = 0
end end
return self return self
end end