Game settings screen, and minor fix on discordRPC

- Uses BG previously from the input config screen, which has gotten a new BG
- Minor tweak on the input config screen to display all inputs names regardless of if they are bound or not
- Added Mod1 function to `funcs.lua`, may be useful again sometime
- Added game settings
  * Manual locking (per gamemode, per ruleset, on harddrop or on softdrop)
  * Piece colours (per ruleset, TTC or Arika)
  * World Reverse toggle
- Moved the discordRPC `libs/` directory, as it's a third party library
- Edited the `discordRPC.lua` file to look for the dll at the right place regardless of how you run the game (until we fuse it that is)

This should have probably been done in several commits, sorry about that
pull/3/head
Oshisaure 2020-10-11 00:42:56 +01:00
parent f28dc08ae2
commit 05230ac046
21 changed files with 245 additions and 66 deletions

View File

@ -62,3 +62,8 @@ function formatBigNum(number)
return string.sub(s, 1, pos) return string.sub(s, 1, pos)
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1") .. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
end end
function Mod1(n, m)
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
return ((n-1) % m) + 1
end

View File

@ -1,5 +1,5 @@
local ffi = require "ffi" local ffi = require "ffi"
local discordRPClib = ffi.load("discord-rpc") local discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc")
ffi.cdef[[ ffi.cdef[[
typedef struct DiscordRichPresence { typedef struct DiscordRichPresence {

View File

@ -20,36 +20,54 @@ backgrounds = {
love.graphics.newImage("res/backgrounds/1800-railways.png"), love.graphics.newImage("res/backgrounds/1800-railways.png"),
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"), love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"), title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
input_config = love.graphics.newImage("res/backgrounds/options-gears.png") input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"),
game_config = love.graphics.newImage("res/backgrounds/options-gears.png"),
} }
blocks = { blocks = {
["2tie"] = { ["2tie"] = {
I = love.graphics.newImage("res/img/s1.png"), R = love.graphics.newImage("res/img/s1.png"),
J = love.graphics.newImage("res/img/s4.png"), O = love.graphics.newImage("res/img/s4.png"),
L = love.graphics.newImage("res/img/s3.png"), Y = love.graphics.newImage("res/img/s7.png"),
O = love.graphics.newImage("res/img/s7.png"), G = love.graphics.newImage("res/img/s6.png"),
S = love.graphics.newImage("res/img/s5.png"), C = love.graphics.newImage("res/img/s2.png"),
T = love.graphics.newImage("res/img/s2.png"), B = love.graphics.newImage("res/img/s3.png"),
Z = love.graphics.newImage("res/img/s6.png"), M = love.graphics.newImage("res/img/s5.png"),
F = love.graphics.newImage("res/img/s9.png"),
G = love.graphics.newImage("res/img/s9.png"),
X = love.graphics.newImage("res/img/s9.png"), X = love.graphics.newImage("res/img/s9.png"),
}, },
["bone"] = { ["bone"] = {
I = love.graphics.newImage("res/img/bone.png"), R = love.graphics.newImage("res/img/bone.png"),
J = love.graphics.newImage("res/img/bone.png"),
L = love.graphics.newImage("res/img/bone.png"),
O = love.graphics.newImage("res/img/bone.png"), O = love.graphics.newImage("res/img/bone.png"),
S = love.graphics.newImage("res/img/bone.png"), Y = love.graphics.newImage("res/img/bone.png"),
T = love.graphics.newImage("res/img/bone.png"),
Z = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
G = love.graphics.newImage("res/img/bone.png"), G = love.graphics.newImage("res/img/bone.png"),
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"), X = love.graphics.newImage("res/img/bone.png"),
} }
} }
ColourSchemes = {
Arika = {
I = "R",
L = "O",
J = "B",
S = "M",
Z = "G",
O = "Y",
T = "C",
},
TTC = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
},
}
for name, blockset in pairs(blocks) do for name, blockset in pairs(blocks) do
for shape, image in pairs(blockset) do for shape, image in pairs(blockset) do
image:setFilter("nearest") image:setFilter("nearest")

View File

@ -1,34 +1,33 @@
discordRPC = require("discordRPC")
appId = "599778517789573120"
function discordRPC.ready(userId, username, discriminator, avatar)
print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar))
end
function discordRPC.disconnected(errorCode, message)
print(string.format("Discord: disconnected (%d: %s)", errorCode, message))
end
function discordRPC.errored(errorCode, message)
print(string.format("Discord: error (%d: %s)", errorCode, message))
end
function discordRPC.joinGame(joinSecret)
print(string.format("Discord: join (%s)", joinSecret))
end
function discordRPC.spectateGame(spectateSecret)
print(string.format("Discord: spectate (%s)", spectateSecret))
end
function discordRPC.joinRequest(userId, username, discriminator, avatar)
print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar))
discordRPC.respond(userId, "yes")
end
function love.load() function love.load()
discordRPC = require("libs.discordRPC")
discordRPC.appId = "599778517789573120"
discordRPC.initialize(appId, true) function discordRPC.ready(userId, username, discriminator, avatar)
print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar))
end
function discordRPC.disconnected(errorCode, message)
print(string.format("Discord: disconnected (%d: %s)", errorCode, message))
end
function discordRPC.errored(errorCode, message)
print(string.format("Discord: error (%d: %s)", errorCode, message))
end
function discordRPC.joinGame(joinSecret)
print(string.format("Discord: join (%s)", joinSecret))
end
function discordRPC.spectateGame(spectateSecret)
print(string.format("Discord: spectate (%s)", spectateSecret))
end
function discordRPC.joinRequest(userId, username, discriminator, avatar)
print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar))
discordRPC.respond(userId, "yes")
end
discordRPC.initialize(discordRPC.appId, true)
local now = os.time(os.date("*t")) local now = os.time(os.date("*t"))
presence = { presence = {
startTimestamp = now, startTimestamp = now,
@ -59,6 +58,12 @@ function love.load()
config.input = {} config.input = {}
scene = InputConfigScene() scene = InputConfigScene()
else else
if not config.gamesettings then config.gamesettings = {} end
for _, option in ipairs(GameConfigScene.options) do
if not config.gamesettings[option[1]] then
config.gamesettings[option[1]] = 1
end
end
if config.current_mode then current_mode = config.current_mode end if config.current_mode then current_mode = config.current_mode end
if config.current_ruleset then current_ruleset = config.current_ruleset end if config.current_ruleset then current_ruleset = config.current_ruleset end
scene = TitleScene() scene = TitleScene()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -11,5 +11,5 @@ ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"
ModeSelectScene = require "scene.mode_select" ModeSelectScene = require "scene.mode_select"
InputConfigScene = require "scene.input_config" InputConfigScene = require "scene.input_config"
ConfigScene = require "scene.config" GameConfigScene = require "scene.game_config"
TitleScene = require "scene.title" TitleScene = require "scene.title"

78
scene/game_config.lua Normal file
View File

@ -0,0 +1,78 @@
local ConfigScene = Scene:extend()
ConfigScene.title = "Game Settings"
require 'load.save'
ConfigScene.options = {
-- this serves as reference to what the options' values mean i guess?
{"manlock", "Manual locking", {"Per gamemode","Per ruleset","Harddrop", "Softdrop"}},
{"piece_colour", "Piece Colours", {"Per ruleset", "Arika", "TTC"}},
{"world_reverse", "World Reverse", {"No", "Yes"}},
}
local optioncount = #ConfigScene.options
function ConfigScene:new()
-- load current config
self.config = config.input
self.highlight = 1
presence.details = "In menus"
presence.state = "Changing game config"
discordRPC.updatePresence(presence)
end
function ConfigScene:update()
end
function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["game_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setFont(font_3x5_4)
love.graphics.print("GAME SETTINGS", 80, 40)
love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22)
love.graphics.setFont(font_3x5_2)
for i, option in ipairs(ConfigScene.options) do
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left")
for j, setting in ipairs(option[3]) do
love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5)
love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center")
end
end
end
function ConfigScene:onKeyPress(e)
if e.scancode == "return" and e.isRepeat == false then
playSE("mode_decide")
saveConfig()
scene = TitleScene()
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
playSE("cursor")
self.highlight = Mod1(self.highlight-1, optioncount)
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
playSE("cursor")
self.highlight = Mod1(self.highlight+1, optioncount)
elseif (e.scancode == config.input["left"] or e.scancode == "left") and e.isRepeat == false then
playSE("cursor_lr")
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3])
elseif (e.scancode == config.input["right"] or e.scancode == "right") and e.isRepeat == false then
playSE("cursor_lr")
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3])
elseif e.scancode == "escape" then
loadSave()
scene = TitleScene()
end
end
return ConfigScene

View File

@ -41,8 +41,8 @@ function ConfigScene:render()
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for i, input in pairs(configurable_inputs) do for i, input in pairs(configurable_inputs) do
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
if config.input[input] then if config.input[input] then
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
love.graphics.printf( love.graphics.printf(
love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")", love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")",
240, 50 + i * 20, 200, "left" 240, 50 + i * 20, 200, "left"
@ -66,8 +66,13 @@ function ConfigScene:onKeyPress(e)
self.input_state = 1 self.input_state = 1
end end
else else
config.input[configurable_inputs[self.input_state]] = e.scancode if e.scancode == "escape" then
self.input_state = self.input_state + 1 loadSave()
scene = TitleScene()
else
config.input[configurable_inputs[self.input_state]] = e.scancode
self.input_state = self.input_state + 1
end
end end
end end

View File

@ -3,6 +3,7 @@ local TitleScene = Scene:extend()
local main_menu_screens = { local main_menu_screens = {
ModeSelectScene, ModeSelectScene,
InputConfigScene, InputConfigScene,
GameConfigScene,
ExitScene, ExitScene,
} }

View File

@ -143,7 +143,7 @@ function Grid:copyBottomRow()
for col = 1, 10 do for col = 1, 10 do
self.grid[24][col] = (self.grid[23][col] == empty) and empty or { self.grid[24][col] = (self.grid[23][col] == empty) and empty or {
skin = self.grid[23][col].skin, skin = self.grid[23][col].skin,
colour = "G" colour = "X"
} }
end end
return true return true
@ -161,7 +161,7 @@ function Grid:applyPiece(piece)
if y + 1 > 0 then if y + 1 > 0 then
self.grid[y+1][x+1] = { self.grid[y+1][x+1] = {
skin = piece.skin, skin = piece.skin,
colour = piece.shape colour = piece.colour
} }
end end
end end
@ -177,7 +177,7 @@ function Grid:applyBigPiece(piece)
if y*2+a > 0 then if y*2+a > 0 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.shape colour = piece.colour
} }
end end
end end

View File

@ -2,7 +2,7 @@ local Object = require 'libs.classic'
local Piece = Object:extend() local Piece = Object:extend()
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, big) function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
self.shape = shape self.shape = shape
self.rotation = rotation self.rotation = rotation
self.position = position self.position = position
@ -10,6 +10,7 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay
self.gravity = gravity self.gravity = gravity
self.lock_delay = lock_delay self.lock_delay = lock_delay
self.skin = skin self.skin = skin
self.colour = colour
self.ghost = false self.ghost = false
self.locked = false self.locked = false
self.big = big self.big = big
@ -21,7 +22,7 @@ function Piece:withOffset(offset)
return Piece( return Piece(
self.shape, self.rotation, self.shape, self.rotation,
{x = self.position.x + offset.x, y = self.position.y + offset.y}, {x = self.position.x + offset.x, y = self.position.y + offset.y},
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
) )
end end
@ -31,7 +32,7 @@ function Piece:withRelativeRotation(rot)
while new_rot >= 4 do new_rot = new_rot - 4 end while new_rot >= 4 do new_rot = new_rot - 4 end
return Piece( return Piece(
self.shape, new_rot, self.position, self.shape, new_rot, self.position,
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
) )
end end
@ -148,13 +149,13 @@ function Piece:draw(opacity, brightness, grid, partial_das)
local y = self.position.y + offset.y local y = self.position.y + offset.y
if self.big then if self.big then
love.graphics.draw( love.graphics.draw(
blocks[self.skin][self.shape], blocks[self.skin][self.colour],
64+x*32+partial_das*2, 16+y*32+gravity_offset*2, 64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
0, 2, 2 0, 2, 2
) )
else else
love.graphics.draw( love.graphics.draw(
blocks[self.skin][self.shape], blocks[self.skin][self.colour],
64+x*16+partial_das, 16+y*16+gravity_offset 64+x*16+partial_das, 16+y*16+gravity_offset
) )
end end

View File

@ -43,6 +43,8 @@ function GameMode:new()
-- variables related to configurable parameters -- variables related to configurable parameters
self.drop_locked = false self.drop_locked = false
self.hard_drop_locked = false self.hard_drop_locked = false
self.lock_on_soft_drop = false
self.lock_on_hard_drop = false
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
self.section_start_time = 0 self.section_start_time = 0
@ -59,6 +61,7 @@ function GameMode:getLineClearDelay() return 40 end
function GameMode:getDasLimit() return 15 end function GameMode:getDasLimit() return 15 end
function GameMode:getNextPiece(ruleset) function GameMode:getNextPiece(ruleset)
return { return {
skin = "2tie", skin = "2tie",
shape = self.randomizer:nextPiece(), shape = self.randomizer:nextPiece(),
@ -72,6 +75,8 @@ function GameMode:initialize(ruleset)
for i = 1, self.next_queue_length do for i = 1, self.next_queue_length do
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
self.lock_on_soft_drop = ({self.instant_soft_drop, ruleset.softdrop_lock, false, true })[config.gamesettings.manlock]
self.lock_on_hard_drop = ({self.instant_hard_drop, ruleset.harddrop_lock, true, false})[config.gamesettings.manlock]
end end
function GameMode:update(inputs, ruleset) function GameMode:update(inputs, ruleset)
@ -125,7 +130,7 @@ function GameMode:update(inputs, ruleset)
self.piece:isDropBlocked(self.grid) and self.piece:isDropBlocked(self.grid) and
not self.hard_drop_locked then not self.hard_drop_locked then
self:onHardDrop(piece_dy) self:onHardDrop(piece_dy)
if self.instant_hard_drop then if self.lock_on_hard_drop then
self.piece.locked = true self.piece.locked = true
end end
end end
@ -134,7 +139,7 @@ function GameMode:update(inputs, ruleset)
self:onSoftDrop(piece_dy) self:onSoftDrop(piece_dy)
if self.piece:isDropBlocked(self.grid) and if self.piece:isDropBlocked(self.grid) and
not self.drop_locked and not self.drop_locked and
self.instant_soft_drop self.lock_on_soft_drop
then then
self.piece.locked = true self.piece.locked = true
end end
@ -340,11 +345,12 @@ function GameMode:drawGhostPiece(ruleset)
end end
function GameMode:drawNextQueue(ruleset) function GameMode:drawNextQueue(ruleset)
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
function drawPiece(piece, skin, offsets, pos_x, pos_y) function drawPiece(piece, skin, offsets, pos_x, pos_y)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
local x = offset.x + ruleset.spawn_positions[piece].x local x = offset.x + ruleset.spawn_positions[piece].x
local y = offset.y + 4.7 local y = offset.y + 4.7
love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16) love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
end end
end end
for i = 1, self.next_queue_length do for i = 1, self.next_queue_length do

View File

@ -110,7 +110,7 @@ function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -172,7 +172,7 @@ function ARS:onPieceRotate(piece, grid)
end end
end end
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -5,6 +5,18 @@ local SRS = Ruleset:extend()
SRS.name = "ACE-SRS" SRS.name = "ACE-SRS"
SRS.hash = "ACE Standard" SRS.hash = "ACE Standard"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true SRS.enable_IRS_wallkicks = true

View File

@ -151,7 +151,7 @@ function ARS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
end end
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end function ARS:get180RotationValue() return 3 end
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
return ARS return ARS

View File

@ -5,6 +5,7 @@ local CRS = Ruleset:extend()
CRS.name = "Cambridge" CRS.name = "Cambridge"
CRS.hash = "Cambridge" CRS.hash = "Cambridge"
CRS.world = true
CRS.spawn_positions = { CRS.spawn_positions = {
I = { x=5, y=4 }, I = { x=5, y=4 },
@ -363,6 +364,10 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial)
if rot_dir == 0 then return end if rot_dir == 0 then return end
if self.world and config.gamesettings.world_reverse == 2 then
rot_dir = 4 - rot_dir
end
local new_piece = piece:withRelativeRotation(rot_dir) local new_piece = piece:withRelativeRotation(rot_dir)
self:attemptWallkicks(piece, new_piece, rot_dir, grid) self:attemptWallkicks(piece, new_piece, rot_dir, grid)
end end

View File

@ -6,6 +6,20 @@ local Ruleset = Object:extend()
Ruleset.name = "" Ruleset.name = ""
Ruleset.hash = "" Ruleset.hash = ""
-- Arika-type ruleset defaults
Ruleset.world = false
Ruleset.colourscheme = {
I = "R",
L = "O",
J = "B",
S = "M",
Z = "G",
O = "Y",
T = "C",
}
Ruleset.softdrop_lock = true
Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false Ruleset.enable_IRS_wallkicks = false
-- Component functions. -- Component functions.
@ -39,6 +53,9 @@ function Ruleset:attemptRotate(new_inputs, piece, grid, initial)
end end
if rot_dir == 0 then return end if rot_dir == 0 then return end
if self.world and config.gamesettings.world_reverse == 2 then
rot_dir = 4 - rot_dir
end
local new_piece = piece:withRelativeRotation(rot_dir) local new_piece = piece:withRelativeRotation(rot_dir)
@ -117,10 +134,12 @@ function Ruleset:initializePiece(
else else
spawn_positions = self.spawn_positions spawn_positions = self.spawn_positions
end end
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
local piece = Piece(data.shape, data.orientation - 1, { local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_positions[data.shape].x, x = spawn_positions[data.shape].x,
y = spawn_positions[data.shape].y y = spawn_positions[data.shape].y
}, self.block_offsets, 0, 0, data.skin, big) }, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
self:onPieceCreate(piece) self:onPieceCreate(piece)
self:rotatePiece(inputs, piece, grid, {}, true) self:rotatePiece(inputs, piece, grid, {}, true)

View File

@ -5,6 +5,18 @@ local SRS = Ruleset:extend()
SRS.name = "Guideline SRS" SRS.name = "Guideline SRS"
SRS.hash = "Standard" SRS.hash = "Standard"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true SRS.enable_IRS_wallkicks = true

View File

@ -5,6 +5,18 @@ local SRS = Ruleset:extend()
SRS.name = "Ti-World" SRS.name = "Ti-World"
SRS.hash = "Bad I-kicks" SRS.hash = "Bad I-kicks"
SRS.world = true
SRS.colourscheme = {
I = "C",
L = "O",
J = "B",
S = "G",
Z = "R",
O = "Y",
T = "M",
}
SRS.softdrop_lock = false
SRS.harddrop_lock = true
SRS.enable_IRS_wallkicks = true SRS.enable_IRS_wallkicks = true