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 = {
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 bgm_locked = false
local unfocused = false
function switchBGM(sound, subsound)
if bgm_locked then return end
if current_bgm ~= nil then
current_bgm:stop()
end
if subsound ~= nil then
current_bgm = bgm[sound][subsound]
resetBGMFadeout()
if bgm_locked or config.bgm_volume <= 0 then
current_bgm = nil
elseif sound ~= nil then
current_bgm = bgm[sound]
resetBGMFadeout()
if subsound ~= nil then
current_bgm = bgm[sound][subsound]
else
current_bgm = bgm[sound]
end
else
current_bgm = nil
end
if current_bgm ~= nil then
resetBGMFadeout()
end
end
function switchBGMLoop(sound, subsound)
if bgm_locked then return end
switchBGM(sound, subsound)
current_bgm:setLooping(true)
if current_bgm then current_bgm:setLooping(true) end
end
function lockBGM()
bgm_locked = true
end
function unlockBGM()
bgm_locked = false
end
local fading_bgm = false
local fadeout_time = 0
local total_fadeout_time = 0
@ -49,11 +57,11 @@ end
function resetBGMFadeout(time)
current_bgm:setVolume(config.bgm_volume)
fading_bgm = false
current_bgm:play()
resumeBGM()
end
function processBGMFadeout(dt)
if fading_bgm then
if current_bgm and fading_bgm then
fadeout_time = fadeout_time - dt
if fadeout_time < 0 then
fadeout_time = 0
@ -63,13 +71,20 @@ function processBGMFadeout(dt)
end
end
function pauseBGM()
function pauseBGM(f)
if f then
unfocused = true
end
if current_bgm ~= nil then
current_bgm:pause()
end
end
function resumeBGM()
function resumeBGM(f)
if f and scene.paused and unfocused then
unfocused = false
return
end
if current_bgm ~= nil then
current_bgm:play()
end

View File

@ -25,6 +25,15 @@ backgrounds = {
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 = {
["2tie"] = {
R = love.graphics.newImage("res/img/s1.png"),
@ -34,6 +43,8 @@ blocks = {
C = love.graphics.newImage("res/img/s2.png"),
B = love.graphics.newImage("res/img/s4.png"),
M = love.graphics.newImage("res/img/s5.png"),
W = love.graphics.newImage("res/img/s9.png"),
D = love.graphics.newImage("res/img/s8.png"),
F = love.graphics.newImage("res/img/s9.png"),
A = love.graphics.newImage("res/img/s8.png"),
X = love.graphics.newImage("res/img/s9.png"),
@ -46,6 +57,8 @@ blocks = {
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
W = love.graphics.newImage("res/img/bone.png"),
D = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
@ -58,13 +71,16 @@ blocks = {
C = love.graphics.newImage("res/img/gem2.png"),
B = love.graphics.newImage("res/img/gem4.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"),
A = love.graphics.newImage("res/img/gem9.png"),
X = love.graphics.newImage("res/img/gem9.png"),
},
["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"),
F = love.graphics.newImage("res/img/squares.png"),
X = love.graphics.newImage("res/img/squares.png"),
}
}
@ -87,7 +103,7 @@ ColourSchemes = {
Z = "R",
O = "Y",
T = "M",
},
}
}
for name, blockset in pairs(blocks) do

View File

@ -6,19 +6,53 @@ function loadSave()
end
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
return {} -- new object
end
return save_data[1]
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()
binser.writeFile('config.sav', config)
love.filesystem.write(
'config.sav', binser.serialize(config)
)
end
function saveHighscores()
binser.writeFile('highscores.sav', highscores)
love.filesystem.write(
'highscores.sav', binser.serialize(highscores)
)
end

View File

@ -22,38 +22,48 @@ sounds = {
go = love.audio.newSource("res/se/go.wav", "static"),
irs = love.audio.newSource("res/se/irs.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!
welcome = love.audio.newSource("res/se/welcomeToCambridge.wav", "static"),
}
function playSE(sound, subsound)
if subsound == nil then
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
sounds[sound]:stop()
if sound ~= nil then
if subsound ~= nil then
sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then
sounds[sound][subsound]:stop()
end
sounds[sound][subsound]:play()
else
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
sounds[sound]:stop()
end
sounds[sound]:play()
end
sounds[sound]:play()
else
sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then
sounds[sound][subsound]:stop()
end
sounds[sound][subsound]:play()
end
end
function playSEOnce(sound, subsound)
if subsound == nil then
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
return
if sound ~= nil then
if subsound ~= nil then
sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then
return
end
sounds[sound][subsound]:play()
else
sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then
return
end
sounds[sound]:play()
end
sounds[sound]:play()
else
sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then
return
end
sounds[sound][subsound]:play()
end
end

1
load/version.lua Normal file
View File

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

281
main.lua
View File

@ -8,106 +8,115 @@ function love.load()
require "load.bgm"
require "load.save"
require "load.bigint"
require "load.version"
loadSave()
require "funcs"
require "scene"
--config["side_next"] = false
--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});
-- used for screenshots
GLOBAL_CANVAS = love.graphics.newCanvas()
-- init config
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
initConfig()
if config.secret == nil then config.secret = false
elseif config.secret == true then playSE("welcome") end
love.window.setFullscreen(config["fullscreen"])
if config.secret then playSE("welcome") end
if not config.gamesettings then
config.gamesettings = {}
config["das_last_key"] = false
-- import custom modules
initModules()
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
config["das_last_key"] = config.gamesettings.das_last_key == 2
--print("Loaded file "..path)
return {type="file",content=require(string.gsub(string.gsub(path,"/","."),"%.lua",""))}
end
for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1
end
function sort_folder_algo(a,b)
local function padnum(d) return ("%03d%s"):format(#d, d) end
if(a.type == "back" or b.type == "back") then
-- back always goes first
return a.type == "back"
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
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
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
scene = TitleScene()
end
function initModules()
game_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
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))
end
end
--]]
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
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))
end
end
]]
--sort mode/rule lists
local function padnum(d) return ("%03d%s"):format(#d, d) end
table.sort(game_modes, function(a,b)
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
table.sort(rulesets, function(a,b)
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
end
local TARGET_FPS = 60
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
sortFolder(game_modes)
sortFolder(rulesets)
end
function love.draw()
love.graphics.setCanvas(GLOBAL_CANVAS)
love.graphics.clear()
love.graphics.push()
-- get offset matrix
@ -122,13 +131,23 @@ function love.draw()
love.graphics.scale(scale_factor)
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.setCanvas()
love.graphics.setColor(1,1,1,1)
love.graphics.draw(GLOBAL_CANVAS)
end
function love.keypressed(key, scancode)
-- global hotkeys
if scancode == "f4" then
if scancode == "f11" then
config["fullscreen"] = not config["fullscreen"]
saveConfig()
love.window.setFullscreen(config["fullscreen"])
elseif scancode == "f2" and scene.title ~= "Input Config" and scene.title ~= "Game" then
scene = InputConfigScene()
@ -140,6 +159,16 @@ function love.keypressed(key, scancode)
scene.restart_message = true
if config.secret then playSE("mode_decide")
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
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return
@ -210,13 +239,13 @@ function love.joystickaxis(joystick, axis, value)
config.input.joysticks[joystick:getName()].axes and
config.input.joysticks[joystick:getName()].axes[axis]
then
if math.abs(value) >= 0.5 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 0.5 and "positive" or "negative"]
if math.abs(value) >= 1 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
end
positive_released = config.input.joysticks[joystick:getName()].axes[axis].positive
negative_released = config.input.joysticks[joystick:getName()].axes[axis].negative
end
if math.abs(value) >= 0.5 then
if math.abs(value) >= 1 then
scene:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
else
scene:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
@ -224,6 +253,14 @@ function love.joystickaxis(joystick, axis, value)
end
end
local last_hat_direction = ""
local directions = {
["u"] = "up",
["d"] = "down",
["l"] = "left",
["r"] = "right",
}
function love.joystickhat(joystick, hat, direction)
local input_pressed = nil
local has_hat = false
@ -240,24 +277,116 @@ function love.joystickhat(joystick, hat, direction)
has_hat = true
end
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
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
scene:onInputRelease({input=config.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
last_hat_direction = ""
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
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
scene:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
last_hat_direction = ""
end
end
function love.wheelmoved(x, y)
scene:onInputPress({input=nil, type="wheel", x=x, y=y})
end
function love.focus(f)
if f and (scene.title ~= "Game" or not scene.paused) then
resumeBGM()
if f then
resumeBGM(true)
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

View File

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

View File

@ -4,23 +4,33 @@ CreditsScene.title = "Credits"
function CreditsScene:new()
self.frames = 0
-- higher = slower
self.scroll_speed = 1.85
switchBGM("credit_roll", "gm3")
DiscordRPC:update({
details = "Watching the credits",
state = "Thanks for playing the game!",
largeImageKey = "ingame-1900",
})
end
function CreditsScene:update()
if love.window.hasFocus() then
self.frames = self.frames + 1
end
if self.frames >= 4200 then
if self.frames >= 2100 * self.scroll_speed then
playSE("mode_decide")
scene = TitleScene()
switchBGM(nil)
elseif self.frames == 3600 then
elseif self.frames == math.floor(1950 * self.scroll_speed) then
fadeoutBGM(2)
end
end
function CreditsScene:render()
local offset = self.frames / self.scroll_speed
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds[19],
@ -29,27 +39,43 @@ function CreditsScene:render()
)
love.graphics.setFont(font_3x5_4)
love.graphics.print("Cambridge Credits", 320, 500 - self.frames / 2)
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(1500 - self.frames / 2, 240))
love.graphics.print("Cambridge Credits", 320, 500 - offset)
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(2050 - offset, 240))
love.graphics.setFont(font_3x5_3)
love.graphics.print("Game Developers", 320, 550 - self.frames / 2)
love.graphics.print("Project Heads", 320, 640 - self.frames / 2)
love.graphics.print("Other Game Developers", 320, 730 - self.frames / 2)
love.graphics.print("Special Thanks", 320, 900 - self.frames / 2)
love.graphics.print("- SashLilac / SpinTriple", 320, math.max(2000 - self.frames / 2, 320))
love.graphics.print("Game Developers", 320, 550 - offset)
love.graphics.print("Project Heads", 320, 640 - offset)
love.graphics.print("Notable Game Developers", 320, 750 - offset)
love.graphics.print("Special Thanks", 320, 1020 - offset)
love.graphics.print("- Milla", 320, math.max(2130 - offset, 320))
love.graphics.setFont(font_3x5_2)
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - self.frames / 2)
love.graphics.print("Mizu\nHailey", 320, 680 - self.frames / 2)
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("Oshisaure\nJoe Zeng", 320, 590 - offset)
love.graphics.print("Mizu\nMarkGamed\nHailey", 320, 680 - offset)
love.graphics.print(
"RocketLanterns\nCylinderKnot\nHammrTime\nKirby703\nMattMayuga\nMyPasswordIsWeak\n" ..
"Nikki Karissa\noffwo\nsinefuse\nTetro48\nTimmSkiller\nuser74003\nAgentBasey\n" ..
"CheeZed_Fish\neightsixfivezero\nEricICX\ngizmo4487\nM1ssing0\nMarkGamed7794\n" ..
"pokemonfan1937\nSimon\nstratus\nZaptorZap\nThe Absolute PLUS Discord\nTetra Legends Discord\n" ..
"Tetra Online Discord\nMultimino Discord\nCambridge Discord\nAnd to you, the player!",
320, 940 - self.frames / 2
"2Tie - TGMsim\nAxel Fox - Multimino\nDr Ocelot - Tetra Legends\n" ..
"Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" ..
"Mine - Tetra Online\nMrZ - Techmino\nosk - TETR.IO\n" ..
"Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" ..
"Rin - Puzzle Trial\nsinefuse - stackfuse",
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

View File

@ -7,10 +7,10 @@ require 'load.save'
function GameScene:new(game_mode, ruleset, inputs)
self.retry_mode = game_mode
self.retry_ruleset = ruleset
self.secret_inputs = copy(inputs)
self.secret_inputs = inputs
self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset()
self.game:initialize(self.ruleset, self.secret_inputs)
self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset)
self.inputs = {
left=false,
right=false,
@ -27,6 +27,7 @@ function GameScene:new(game_mode, ruleset, inputs)
DiscordRPC:update({
details = self.game.rpc_details,
state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
@ -38,94 +39,39 @@ function GameScene:update()
end
self.game:update(inputs, self.ruleset)
self.game.grid:update()
DiscordRPC:update({
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
end
function GameScene:render()
love.graphics.setColor(1, 1, 1, 1)
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
self.game:draw(self.paused)
end
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_hash = self.game.hash .. "-" .. self.ruleset.hash
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()
elseif e.input == "retry" then
switchBGM(nil)
self.game:onExit()
scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs)
elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
self.paused = not self.paused
if self.paused then pauseBGM()
else resumeBGM() end
elseif e.input == "menu_back" then
self.game:onExit()
scene = ModeSelectScene()
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = true

View File

@ -11,13 +11,13 @@ ConfigScene.options = {
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
{"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"}},
{"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"}},
{"synchroes_allowed", "Synchroes", false, {"Per ruleset", "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"},
{"bgm_volume", "BGM", true, "bgmSlider"},
}
@ -29,7 +29,7 @@ function ConfigScene:new()
self.highlight = 1
DiscordRPC:update({
details = "In menus",
details = "In settings",
state = "Changing game settings",
})
@ -38,7 +38,6 @@ function ConfigScene:new()
end
function ConfigScene:update()
config["das_last_key"] = config.gamesettings.das_last_key == 2
self.sfxSlider:update()
self.bgmSlider:update()
end
@ -101,9 +100,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[4])
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")
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
elseif e.input == "right" or e.scancode == "right" then
if not self.options[self.highlight][3] then
@ -111,9 +111,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[4])
else
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)))--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
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave()

View File

@ -2,161 +2,65 @@ local ConfigScene = Scene:extend()
ConfigScene.title = "Input 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 menu_screens = {
KeyConfigScene,
StickConfigScene
}
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()
self.input_state = 1
self.set_inputs = newSetInputs()
self.new_input = {}
self.axis_timer = 0
DiscordRPC:update({
details = "In menus",
state = "Changing input config",
})
self.menu_state = 1
DiscordRPC:update({
details = "In settings",
state = "Changing input config",
largeImageKey = "settings-input"
})
end
function ConfigScene:update()
end
function ConfigScene:update() end
function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
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 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
love.graphics.setFont(font_3x5_4)
love.graphics.print("INPUT CONFIG", 80, 40)
self.axis_timer = self.axis_timer + 1
love.graphics.setFont(font_3x5_2)
love.graphics.print("Which controls do you want to configure?", 80, 90)
love.graphics.setColor(1, 1, 1, 0.5)
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
local function addJoystick(input, name)
if not input.joysticks then
input.joysticks = {}
end
if not input.joysticks[name] then
input.joysticks[name] = {}
end
function ConfigScene:changeOption(rel)
local len = table.getn(menu_screens)
self.menu_state = (self.menu_state + len + rel - 1) % len + 1
end
function ConfigScene:onInputPress(e)
if e.type == "key" then
-- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" and config.input then
-- cancel only if there was an input config already
scene = 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
if e.input == "menu_decide" or e.scancode == "return" then
playSE("main_decide")
scene = menu_screens[self.menu_state]()
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()
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
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 = {
mode = current_mode,
mode_list = game_modes,
mode_list_history = {},
mode_path = (config.mode_path or {}),
ruleset = current_ruleset,
rule_list = rulesets,
rule_list_history = {},
rule_path = config.rule_path,
select = "mode",
}
self.secret_inputs = {
rotate_left = false,
rotate_left2 = false,
rotate_right = false,
rotate_right2 = false,
rotate_180 = false,
hold = false,
}
self.secret_inputs = {}
self.das = 0
DiscordRPC:update({
details = "In menus",
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
function ModeSelectScene:update()
switchBGM(nil) -- experimental
if self.das_up or self.das_down then
self.das = self.das + 1
else
self.das = 0
end
if self.das >= 15 then
self:changeOption(self.das_up and -1 or 1)
self.das = self.das - 4
end
DiscordRPC:update({
details = "In menus",
state = "Choosing a " .. self.menu_state.select,
largeImageKey = "ingame-000"
})
end
function ModeSelectScene:render()
@ -36,6 +122,23 @@ function ModeSelectScene:render()
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
love.graphics.setColor(1, 1, 1, 0.5)
elseif self.menu_state.select == "ruleset" then
@ -52,39 +155,87 @@ function ModeSelectScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
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
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
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
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
function ModeSelectScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" then
current_mode = self.menu_state.mode
current_ruleset = self.menu_state.ruleset
config.current_mode = current_mode
config.current_ruleset = current_ruleset
playSE("mode_decide")
saveConfig()
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs)
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_ruleset = self.menu_state.ruleset
config.current_mode = current_mode
config.mode_path = self.menu_state.mode_path
config.current_ruleset = current_ruleset
config.rule_path = self.menu_state.rule_path
playSE("mode_decide")
saveConfig()
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
self:changeOption(-1)
playSE("cursor")
self.das_up = true
self.das_down = nil
elseif e.input == "down" or e.scancode == "down" then
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
self:switchSelect()
playSE("cursor_lr")
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene()
elseif e.input then
@ -93,7 +244,11 @@ function ModeSelectScene:onInputPress(e)
end
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
end
end
@ -104,24 +259,26 @@ function ModeSelectScene:changeOption(rel)
elseif self.menu_state.select == "ruleset" then
self:changeRuleset(rel)
end
playSE("cursor")
end
function ModeSelectScene:switchSelect(rel)
function ModeSelectScene:switchSelect()
if self.menu_state.select == "mode" then
self.menu_state.select = "ruleset"
elseif self.menu_state.select == "ruleset" then
self.menu_state.select = "mode"
end
playSE("cursor_lr")
end
function ModeSelectScene:changeMode(rel)
local len = table.getn(game_modes)
self.menu_state.mode = (self.menu_state.mode + len + rel - 1) % len + 1
local len = table.getn(self.menu_state.mode_list)
self.menu_state.mode = Mod1(self.menu_state.mode + rel, len)
end
function ModeSelectScene:changeRuleset(rel)
local len = table.getn(rulesets)
self.menu_state.ruleset = (self.menu_state.ruleset + len + rel - 1) % len + 1
local len = table.getn(self.menu_state.rule_list)
self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len)
end
return ModeSelectScene

View File

@ -8,11 +8,20 @@ local menu_screens = {
TuningScene
}
local settingsidle = {
"Tweaking some knobs",
"Tuning up",
"Adjusting options",
"Setting up",
"Setting the settings"
}
function SettingsScene:new()
self.menu_state = 1
DiscordRPC:update({
details = "In menus",
state = "Changing settings",
details = "In settings",
state = settingsidle[math.random(#settingsidle)],
largeImageKey = "settings",
})
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",
"In menus",
"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()
@ -35,6 +44,8 @@ function TitleScene:new()
DiscordRPC:update({
details = "In menus",
state = mainmenuidle[math.random(#mainmenuidle)],
largeImageKey = "icon2",
largeImageText = version
})
end
@ -82,7 +93,6 @@ function TitleScene:render()
for i, screen in pairs(main_menu_screens) do
love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left")
end
end
function TitleScene:changeOption(rel)
@ -106,6 +116,9 @@ function TitleScene:onInputPress(e)
self.text = self.text .. (e.scancode ~= nil and e.scancode or "")
if self.text == "ffffff" then
self.text_flag = true
DiscordRPC:update({
largeImageKey = "snow"
})
end
end
end

View File

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

View File

@ -111,18 +111,24 @@ function Grid:getClearedRowCount()
end
function Grid:markClearedRows()
local block_table = {}
for row = 1, self.height do
if self:isRowFull(row) then
block_table[row] = {}
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] = {
skin = self.grid[row][x].skin,
colour = "X"
}
self.grid_age[row][x] = 0
--self.grid_age[row][x] = 0
end
end
end
return true
return block_table
end
function Grid:clearClearedRows()
@ -201,7 +207,7 @@ function Grid:applyBigPiece(piece)
y = piece.position.y + offset.y
for a = 1, 2 do
for b = 1, 2 do
if y*2+a > 0 then
if y*2+a > 0 and y*2 < self.height then
self.grid[y*2+a][x*2+b] = {
skin = piece.skin,
colour = piece.colour
@ -343,7 +349,7 @@ function Grid:markSquares()
elseif i == 2 then
for j = 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"
end
@ -388,15 +394,16 @@ end
function Grid:draw()
for y = 5, self.height 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
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
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)
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
love.graphics.setColor(0.5, 0.5, 0.5, 1)
end
@ -405,11 +412,11 @@ function Grid:draw()
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.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)
end
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)
end
if x > 1 and self.grid[y][x-1] == empty then
@ -427,18 +434,14 @@ end
function Grid:drawOutline()
for y = 5, self.height 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
love.graphics.setColor(0.8, 0.8, 0.8, 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)
end
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)
end
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
if self.grid[y][x] ~= empty 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
opacity = garbage_opacity_function(self.grid_age[y][x])
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
love.graphics.setColor(0.64, 0.64, 0.64)
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)
end
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)
end
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
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
A = 1 - self.grid_age[y][x] / 15
A = 0
end
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)
if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline)
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)
end
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)
end
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.gravity = 0
if self.position.y > piece_y then
-- if it got dropped any, also reset lock delay
if self.ghost == false then playSE("bottom") end
self.lock_delay = 0
-- self.lock_delay = 0
end
return self
end
@ -118,19 +117,37 @@ function Piece:lockIfBottomed(grid)
return self
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
if self:isDropBlocked(grid) then
self.gravity = math.min(1, new_gravity)
self.lock_delay = self.lock_delay + 1
else
local dropped_squares = math.floor(new_gravity)
local new_frac_gravity = new_gravity - dropped_squares
self.gravity = new_frac_gravity
self:dropSquares(dropped_squares, grid)
if self:isDropBlocked(grid) then
playSE("bottom")
if classic_lock then
self.gravity = new_gravity
else
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
self.gravity = new_frac_gravity
self:dropSquares(dropped_squares, grid)
if self:isDropBlocked(grid) then
playSE("bottom")
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
return self
end