Compare commits

..

65 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
f22424d671 Update README.md - loading custom assets 2021-01-10 13:39:28 -05:00
Ishaan Bhardwaj
dd6baf1fe6 Draw outline now has line clear anim 2021-01-10 11:41:34 -05:00
Ishaan Bhardwaj
11cf5a9d55 Spawn SE bugfix 2021-01-10 11:15:36 -05:00
Ishaan Bhardwaj
5642ed1326 Added a ruleset toggle for ARE. 2021-01-09 23:17:24 -05:00
Ishaan Bhardwaj
c0888c484f Fixed the first easter egg 2021-01-08 20:33:44 -05:00
Ishaan Bhardwaj
3ef3b193fd 3-tall pentoes spawn highest on 21 now 2021-01-08 17:16:15 -05:00
Ishaan Bhardwaj
0c2e3efd1a PAIRS anti-stall added 2021-01-08 16:46:19 -05:00
Ishaan Bhardwaj
5076adf022 Secret inputs fix 2021-01-08 13:59:42 -05:00
Ishaan Bhardwaj
1a75d983dc Corrected PAIRS big spawns 2021-01-07 20:53:36 -05:00
Ishaan Bhardwaj
5b8e9586bd Sakura bugfixes 2021-01-07 19:59:11 -05:00
Ishaan Bhardwaj
7d7dd8c3c2 Roll roll bugfixes 2021-01-07 19:52:36 -05:00
Ishaan Bhardwaj
29afdcecfc PAIRS I5 and U spawns fixed 2021-01-07 19:06:37 -05:00
Ishaan Bhardwaj
8b09833ae6 PAIRS added, with bugfixes 2021-01-07 18:42:49 -05:00
Ishaan Bhardwaj
64047eaf9c Slight randomizer logic change, PAIRS incoming 2021-01-07 16:53:46 -05:00
Ishaan Bhardwaj
125488b4d9 Can no longer buffer a hard drop when not allowed 2021-01-06 23:06:51 -05:00
Ishaan Bhardwaj
1fdd091456 Ruleset and randomizer refactoring (Read comments)
You can now specify an arbitrary number of pieces for a ruleset.
The randomizers will adjust accordingly.
Expect a pento ruleset in the modpack soon!
Also, gamemode skin selection has been refactored.
2021-01-06 22:53:44 -05:00
Ishaan Bhardwaj
ced40297cc Line clear anim part 3 2021-01-06 21:37:51 -05:00
Ishaan Bhardwaj
32f2a0b3e7 Line clear anim part 2 2021-01-06 18:01:56 -05:00
Ishaan Bhardwaj
dd5347ad8d (Beta) line clear animation 2021-01-06 16:56:44 -05:00
Ishaan Bhardwaj
b732ebb213 Credits scene no longer plays while not focused 2021-01-06 16:10:01 -05:00
Ishaan Bhardwaj
84634d6933 Added an option to control buffer locking.
You can now choose if you want a drop input
during ARE to lock the piece on the first frame it is active.
2021-01-06 16:06:17 -05:00
Ishaan Bhardwaj
0d13a9f236 Can send inputs from mode select to game
Warning: this may break some things
2021-01-05 21:59:50 -05:00
Ishaan Bhardwaj
45120bc9f7 Update README MacOS instructions 2021-01-05 08:59:22 -05:00
Ishaan Bhardwaj
57c7d9c4c3 v0.3-beta1: Sakura done 2021-01-04 18:01:29 -05:00
Ishaan Bhardwaj
9f52d8bf10 relocated non bgm to modpack 2021-01-04 14:43:39 -05:00
Ishaan Bhardwaj
57bd6a8286 Almost done with Sakura 2021-01-03 23:18:57 -05:00
Oshisaure
1a68cd8fce More work on sakura, Xray and colour block effects implemented. Added Grid:drawCustom() to handle custom opacity/tint effects. 2021-01-03 00:05:54 +00:00
Ishaan Bhardwaj
56baf46839 Sakura *should* be complete now, please bug-report 2021-01-02 14:31:53 -05:00
Ishaan Bhardwaj
305d07e10a Sakura beta v2.k 2021-01-02 14:10:01 -05:00
Ishaan Bhardwaj
8d954cabc2 Sakura Beta v2.j 2021-01-02 12:56:52 -05:00
Ishaan Bhardwaj
0281220ea0 Sakura Beta v2.i 2021-01-02 12:51:00 -05:00
Ishaan Bhardwaj
aef5d88d3f Sakura Beta v2.h 2021-01-02 12:36:26 -05:00
Ishaan Bhardwaj
3676f7697c Sakura Beta v2 2021-01-02 12:21:10 -05:00
Ishaan Bhardwaj
acb0eb1a71 Sakura mode beta 2020-12-30 15:19:53 -05:00
Ishaan Bhardwaj
a89bf05cab Fixed an issue with controllers on the menu 2020-12-29 22:55:51 -05:00
Ishaan Bhardwaj
8008315994 Condensed ARE canceling code a bit 2020-12-29 14:00:11 -05:00
Ishaan Bhardwaj
90f62cb7dd Refactored ARE cancel 2020-12-28 23:32:41 -05:00
Oshisaure
eaee5fc7f0 Tweaked rotation/manipulation behaviour on SRS rules.
Also changed order of operations to call onPieceRotate in Rulesets after actually rotating the piece.
2020-12-28 03:41:26 +00:00
Ishaan Bhardwaj
e3b038b5a7 A festive easter egg has arrived! (v0.2.6.1)
Good luck hunting for the egg!
2020-12-24 22:58:06 -05:00
Ishaan Bhardwaj
083693496e Grid piece placement conditions 2020-12-22 22:04:06 -05:00
Ishaan Bhardwaj
ba576dfc77 Allow sliders to be controlled with keyboard
Credits to Phoenix Flare
2020-12-22 14:43:59 -05:00
Ishaan Bhardwaj
e195ccd721 Marathon A3 fixes 2020-12-21 23:32:39 -05:00
Ishaan Bhardwaj
70f703eb2f Fixed piece fade out when paused 2020-12-21 16:20:25 -05:00
Ishaan Bhardwaj
dc4d4a8259 Credits now stops music when you exit the screen 2020-12-21 16:00:03 -05:00
Ishaan Bhardwaj
565510c7b2 Credits and credit roll music updated 2020-12-21 15:48:34 -05:00
Ishaan Bhardwaj
c26a3f37de Update README.md 2020-12-20 20:35:36 -05:00
Ishaan Bhardwaj
0c1ce2f717 Fix package script not packing slider lib 2020-12-20 20:06:16 -05:00
Ishaan Bhardwaj
f14ab2a328 BGM focus fix 2020-12-20 16:55:34 -05:00
Ishaan Bhardwaj
042dbd220b text was slightly off-center 2020-12-20 15:31:42 -05:00
Ishaan Bhardwaj
548612123a SFX and BGM are now separate sliders 2020-12-20 15:26:32 -05:00
Ishaan Bhardwaj
f4675da0b0 Unlock and fix BGM, add pause button 2020-12-20 15:08:53 -05:00
Ishaan Bhardwaj
511e9592bc Fixed next piece sounds not playing 2020-12-20 10:47:24 -05:00
Ishaan Bhardwaj
5f3990ff58 Small credits update 2020-12-20 10:35:05 -05:00
Ishaan Bhardwaj
50ff4adf27 Credits scene <3 2020-12-20 10:28:34 -05:00
Ishaan Bhardwaj
87b88f4b42 Refactored settings menus 2020-12-20 09:45:49 -05:00
Ishaan Bhardwaj
130c2ea403 Easier easter egg #2 2020-12-19 20:44:24 -05:00
Ishaan Bhardwaj
1ea304916e Made it easier to see the egg 2020-12-19 20:43:57 -05:00
Ishaan Bhardwaj
e26b094830 A little easter egg... 2020-12-19 20:31:14 -05:00
Ishaan Bhardwaj
bcb44725bf Cambridge RS fix lock in midair 2020-12-19 14:04:08 -05:00
Ishaan Bhardwaj
2990844c52 Adjusted tuning scene 2020-12-18 23:17:53 -05:00
Ishaan Bhardwaj
c343014d6f Tuning scene 2020-12-18 21:28:30 -05:00
Ishaan Bhardwaj
605add7e94 Added customizable DAS and ARR! (read comments)
This only applies to modes that allow it.
This feature does not apply to main modes (yet)
2020-12-18 21:25:09 -05:00
Ishaan Bhardwaj
d3b647ca71 Fixed certain rulesets locking in midair 2020-12-18 21:24:10 -05:00
Ishaan Bhardwaj
1101aa467d Smooth piece drop 2020-12-17 18:00:07 -05:00
Ishaan Bhardwaj
ce27a7ed18 Update Tetra Online README 2020-12-17 10:51:08 -05:00
48 changed files with 1726 additions and 153 deletions

View File

@@ -1,11 +1,5 @@
![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png) ![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png)
Important notice
================
Tetra Online was recently struck by an illegal DMCA claim from the Tetris Company.
Please, spread awareness of this, and use the hashtag #DMCAgaming in your posts.
Cambridge Cambridge
========= =========
@@ -66,6 +60,14 @@ Then, check the mod pack section at the bottom of this page.
If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!** If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!**
#### Downloading a release
You can download the .love file in the latest release, and run it with:
love cambridge.love
#### Installing from source
Clone the repository in git: Clone the repository in git:
git clone https://github.com/SashLilac/cambridge git clone https://github.com/SashLilac/cambridge
@@ -82,6 +84,10 @@ It should run automatically!
Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically. Simply drag your mode, ruleset, and randomizer Lua files into their respective [directory](https://love2d.org/wiki/love.filesystem), and they should appear automatically.
You can also load custom assets through this way, assuming you preserve the directory structure.
**WARNING:** The .exe / .love files and the bleeding edge releases have different save directories. Read the above link carefully!
For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential. For more detailed instructions, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
License License

View File

@@ -45,7 +45,7 @@ Other sounds from:
Music Music
----- -----
1. TGM3 credit roll music. 1. Second Reality opening scene music (1993).
2. The FitnessGram™ Pacer Test. 2. The FitnessGram™ Pacer Test.
All background music is (currently) only unofficially included. In later releases they may be replaced with specifically licensed music as applicable. All background music is (currently) only unofficially included. In later releases they may be replaced with specifically licensed music as applicable.
@@ -114,3 +114,29 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
simple-slider (https://love2d.org/forums/viewtopic.php?t=80711)
--------------------
Copyright (c) 2016 George Prosser
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

138
libs/simple-slider.lua Normal file
View File

@@ -0,0 +1,138 @@
--[[
Copyright (c) 2016 George Prosser
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
]]
local slider = {}
slider.__index = slider
function newSlider(x, y, length, value, min, max, setter, style)
local s = {}
s.value = (value - min) / (max - min)
s.min = min
s.max = max
s.setter = setter
s.x = x
s.y = y
s.length = length
local p = style or {}
s.width = p.width or length * 0.1
s.orientation = p.orientation or 'horizontal'
s.track = p.track or 'rectangle'
s.knob = p.knob or 'rectangle'
s.grabbed = false
s.wasDown = true
s.ox = 0
s.oy = 0
return setmetatable(s, slider)
end
function slider:update(mouseX, mouseY, mouseDown)
local x = mouseX or love.mouse.getX()
local y = mouseY or love.mouse.getY()
local down = love.mouse.isDown(1)
if mouseDown ~= nil then
down = mouseDown
end
local knobX = self.x
local knobY = self.y
if self.orientation == 'horizontal' then
knobX = self.x - self.length/2 + self.length * self.value
elseif self.orientation == 'vertical' then
knobY = self.y + self.length/2 - self.length * self.value
end
local ox = x - knobX
local oy = y - knobY
local dx = ox - self.ox
local dy = oy - self.oy
if down then
if self.grabbed then
if self.orientation == 'horizontal' then
self.value = self.value + dx / self.length
elseif self.orientation == 'vertical' then
self.value = self.value - dy / self.length
end
elseif (x > knobX - self.width/2 and x < knobX + self.width/2 and y > knobY - self.width/2 and y < knobY + self.width/2) and not self.wasDown then
self.ox = ox
self.oy = oy
self.grabbed = true
end
else
self.grabbed = false
end
self.value = math.max(0, math.min(1, self.value))
if self.setter ~= nil then
self.setter(self.min + self.value * (self.max - self.min))
end
self.wasDown = down
end
function slider:draw()
if self.track == 'rectangle' then
if self.orientation == 'horizontal' then
love.graphics.rectangle('line', self.x - self.length/2 - self.width/2, self.y - self.width/2, self.length + self.width, self.width)
elseif self.orientation == 'vertical' then
love.graphics.rectangle('line', self.x - self.width/2, self.y - self.length/2 - self.width/2, self.width, self.length + self.width)
end
elseif self.track == 'line' then
if self.orientation == 'horizontal' then
love.graphics.line(self.x - self.length/2, self.y, self.x + self.length/2, self.y)
elseif self.orientation == 'vertical' then
love.graphics.line(self.x, self.y - self.length/2, self.x, self.y + self.length/2)
end
elseif self.track == 'roundrect' then
if self.orientation == 'horizontal' then
love.graphics.rectangle('line', self.x - self.length/2 - self.width/2, self.y - self.width/2, self.length + self.width, self.width, self.width/2, self.width)
elseif self.orientation == 'vertical' then
love.graphics.rectangle('line', self.x - self.width/2, self.y - self.length/2 - self.width/2, self.width, self.length + self.width, self.width, self.width/2)
end
end
local knobX = self.x
local knobY = self.y
if self.orientation == 'horizontal' then
knobX = self.x - self.length/2 + self.length * self.value
elseif self.orientation == 'vertical' then
knobY = self.y + self.length/2 - self.length * self.value
end
if self.knob == 'rectangle' then
love.graphics.rectangle('fill', knobX - self.width/2, knobY - self.width/2, self.width, self.width)
elseif self.knob == 'circle' then
love.graphics.circle('fill', knobX, knobY, self.width/2)
end
end
function slider:getValue()
return self.min + self.value * (self.max - self.min)
end

View File

@@ -6,7 +6,7 @@ bgm = {
} }
local current_bgm = nil local current_bgm = nil
local bgm_locked = true local bgm_locked = false
function switchBGM(sound, subsound) function switchBGM(sound, subsound)
if bgm_locked then return end if bgm_locked then return end
@@ -47,7 +47,7 @@ function fadeoutBGM(time)
end end
function resetBGMFadeout(time) function resetBGMFadeout(time)
current_bgm:setVolume(1) current_bgm:setVolume(config.bgm_volume)
fading_bgm = false fading_bgm = false
current_bgm:play() current_bgm:play()
end end
@@ -59,7 +59,7 @@ function processBGMFadeout(dt)
fadeout_time = 0 fadeout_time = 0
fading_bgm = false fading_bgm = false
end end
current_bgm:setVolume(fadeout_time / total_fadeout_time) current_bgm:setVolume(fadeout_time * config.bgm_volume / total_fadeout_time)
end end
end end

View File

@@ -20,6 +20,7 @@ backgrounds = {
love.graphics.newImage("res/backgrounds/1800.png"), love.graphics.newImage("res/backgrounds/1800.png"),
love.graphics.newImage("res/backgrounds/1900.png"), love.graphics.newImage("res/backgrounds/1900.png"),
title = love.graphics.newImage("res/backgrounds/title.png"), title = love.graphics.newImage("res/backgrounds/title.png"),
snow = love.graphics.newImage("res/backgrounds/snow.png"),
input_config = love.graphics.newImage("res/backgrounds/options-input.png"), input_config = love.graphics.newImage("res/backgrounds/options-input.png"),
game_config = love.graphics.newImage("res/backgrounds/options-game.png"), game_config = love.graphics.newImage("res/backgrounds/options-game.png"),
} }
@@ -48,6 +49,18 @@ blocks = {
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"),
},
["gem"] = {
R = love.graphics.newImage("res/img/gem1.png"),
O = love.graphics.newImage("res/img/gem3.png"),
Y = love.graphics.newImage("res/img/gem7.png"),
G = love.graphics.newImage("res/img/gem6.png"),
C = love.graphics.newImage("res/img/gem2.png"),
B = love.graphics.newImage("res/img/gem4.png"),
M = love.graphics.newImage("res/img/gem5.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"),
} }
} }
@@ -84,4 +97,5 @@ misc_graphics = {
go = love.graphics.newImage("res/img/go.png"), go = love.graphics.newImage("res/img/go.png"),
select_mode = love.graphics.newImage("res/img/select_mode.png"), select_mode = love.graphics.newImage("res/img/select_mode.png"),
strike = love.graphics.newImage("res/img/strike.png"), strike = love.graphics.newImage("res/img/strike.png"),
santa = love.graphics.newImage("res/img/santa.png")
} }

View File

@@ -22,17 +22,19 @@ 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"),
-- a secret sound!
welcome = love.audio.newSource("res/se/welcomeToCambridge.wav", "static"),
} }
function playSE(sound, subsound) function playSE(sound, subsound)
if subsound == nil then if subsound == nil then
sounds[sound]:setVolume(0.5) sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then if sounds[sound]:isPlaying() then
sounds[sound]:stop() sounds[sound]:stop()
end end
sounds[sound]:play() sounds[sound]:play()
else else
sounds[sound][subsound]:setVolume(0.6) 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
@@ -42,13 +44,13 @@ end
function playSEOnce(sound, subsound) function playSEOnce(sound, subsound)
if subsound == nil then if subsound == nil then
sounds[sound]:setVolume(0.5) sounds[sound]:setVolume(config.sfx_volume)
if sounds[sound]:isPlaying() then if sounds[sound]:isPlaying() then
return return
end end
sounds[sound]:play() sounds[sound]:play()
else else
sounds[sound][subsound]:setVolume(0.5) sounds[sound][subsound]:setVolume(config.sfx_volume)
if sounds[sound][subsound]:isPlaying() then if sounds[sound][subsound]:isPlaying() then
return return
end end

View File

@@ -9,12 +9,20 @@ function love.load()
require "load.save" require "load.save"
loadSave() loadSave()
require "scene" require "scene"
config["side_next"] = false --config["side_next"] = false
config["reverse_rotate"] = true --config["reverse_rotate"] = true
config["fullscreen"] = 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});
if not config.das then config.das = 10 end
if not config.arr then config.arr = 2 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
elseif config.secret == true then playSE("welcome") end
if not config.gamesettings then if not config.gamesettings then
config.gamesettings = {} config.gamesettings = {}
config["das_last_key"] = false config["das_last_key"] = false
@@ -121,6 +129,14 @@ function love.keypressed(key, scancode)
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()
switchBGM(nil)
-- secret sound playing :eyes:
elseif scancode == "f8" and scene.title == "Title" then
config.secret = not config.secret
saveConfig()
scene.restart_message = true
if config.secret then playSE("mode_decide")
else playSE("erase") end
-- 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
@@ -236,7 +252,7 @@ function love.joystickhat(joystick, hat, direction)
end end
function love.focus(f) function love.focus(f)
if f then if f and (scene.title ~= "Game" or not scene.paused) then
resumeBGM() resumeBGM()
else else
pauseBGM() pauseBGM()

View File

@@ -1,2 +1,2 @@
tar -a -c -f cambridge.zip libs/binser.lua libs/classic.lua libs/discordRPC.lua load res scene tetris conf.lua main.lua scene.lua funcs.lua tar -a -c -f cambridge.zip libs/binser.lua libs/classic.lua libs/simple-slider.lua libs/discordRPC.lua load res scene tetris conf.lua main.lua scene.lua funcs.lua
rename cambridge.zip cambridge.love rename cambridge.zip cambridge.love

BIN
res/backgrounds/snow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

BIN
res/img/gem1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

BIN
res/img/gem2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

BIN
res/img/gem3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

BIN
res/img/gem4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

BIN
res/img/gem5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

BIN
res/img/gem6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

BIN
res/img/gem7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

BIN
res/img/gem9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

BIN
res/img/santa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

View File

@@ -13,4 +13,7 @@ GameScene = require "scene.game"
ModeSelectScene = require "scene.mode_select" ModeSelectScene = require "scene.mode_select"
InputConfigScene = require "scene.input_config" InputConfigScene = require "scene.input_config"
GameConfigScene = require "scene.game_config" GameConfigScene = require "scene.game_config"
TuningScene = require "scene.tuning"
SettingsScene = require "scene.settings"
CreditsScene = require "scene.credits"
TitleScene = require "scene.title" TitleScene = require "scene.title"

64
scene/credits.lua Normal file
View File

@@ -0,0 +1,64 @@
local CreditsScene = Scene:extend()
CreditsScene.title = "Credits"
function CreditsScene:new()
self.frames = 0
switchBGM("credit_roll", "gm3")
end
function CreditsScene:update()
if love.window.hasFocus() then
self.frames = self.frames + 1
end
if self.frames >= 4200 then
playSE("mode_decide")
scene = TitleScene()
switchBGM(nil)
elseif self.frames == 3600 then
fadeoutBGM(2)
end
end
function CreditsScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds[19],
0, 0, 0,
0.5, 0.5
)
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.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.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(
"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
)
end
function CreditsScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" or
e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene()
switchBGM(nil)
end
end
return CreditsScene

View File

@@ -4,12 +4,13 @@ GameScene.title = "Game"
require 'load.save' require 'load.save'
function GameScene:new(game_mode, ruleset) 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.game = game_mode() self.secret_inputs = copy(inputs)
self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset() self.ruleset = ruleset()
self.game:initialize(self.ruleset) self.game:initialize(self.ruleset, self.secret_inputs)
self.inputs = { self.inputs = {
left=false, left=false,
right=false, right=false,
@@ -22,6 +23,7 @@ function GameScene:new(game_mode, ruleset)
rotate_180=false, rotate_180=false,
hold=false, hold=false,
} }
self.paused = false
DiscordRPC:update({ DiscordRPC:update({
details = self.game.rpc_details, details = self.game.rpc_details,
state = self.game.name, state = self.game.name,
@@ -29,15 +31,14 @@ function GameScene:new(game_mode, ruleset)
end end
function GameScene:update() function GameScene:update()
if love.window.hasFocus() then if love.window.hasFocus() and not self.paused then
local inputs = {} local inputs = {}
for input, value in pairs(self.inputs) do for input, value in pairs(self.inputs) do
inputs[input] = value inputs[input] = value
end end
self.game:update(inputs, self.ruleset) self.game:update(inputs, self.ruleset)
self.game.grid:update()
end end
self.game.grid:update()
end end
function GameScene:render() function GameScene:render()
@@ -72,6 +73,9 @@ function GameScene:render()
if config.gamesettings.display_gamemode == 1 then if config.gamesettings.display_gamemode == 1 then
love.graphics.printf(self.game.name .. " - " .. self.ruleset.name, 0, 460, 640, "left") love.graphics.printf(self.game.name .. " - " .. self.ruleset.name, 0, 460, 640, "left")
end end
love.graphics.setFont(font_3x5_3)
if self.paused then love.graphics.print("PAUSED!", 80, 100) end
end end
function GameScene:onInputPress(e) function GameScene:onInputPress(e)
@@ -79,9 +83,13 @@ function GameScene:onInputPress(e)
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)
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset) 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
scene = GameScene(self.retry_mode, self.retry_ruleset) 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 elseif e.input == "menu_back" then
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

View File

@@ -3,17 +3,22 @@ local ConfigScene = Scene:extend()
ConfigScene.title = "Game Settings" ConfigScene.title = "Game Settings"
require 'load.save' require 'load.save'
require 'libs.simple-slider'
ConfigScene.options = { ConfigScene.options = {
-- this serves as reference to what the options' values mean i guess? -- this serves as reference to what the options' values mean i guess?
{"manlock", "Manual locking",{"Per ruleset","Per gamemode","Harddrop", "Softdrop"}}, -- Format: {name in config, displayed name, uses slider?, options OR slider name}
{"piece_colour", "Piece Colours", {"Per ruleset","Arika" ,"TTC"}}, {"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
{"world_reverse","A Button Rotation", {"Left" ,"Auto" ,"Right"}}, {"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
{"display_gamemode", "Display Gamemode", {"On", "Off"}}, {"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
{"next_se", "Next Piece SFX", {"On", "Off"}}, {"display_gamemode", "Display Gamemode", false, {"On", "Off"}},
{"das_last_key", "DAS Switch", {"Default", "Instant"}}, {"das_last_key", "DAS Switch", false, {"Default", "Instant"}},
{"synchroes_allowed", "Synchroes", {"Per ruleset", "On", "Off"}}, {"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
{"diagonal_input", "Diagonal Input", {"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"}},
{"sfx_volume", "SFX", true, "sfxSlider"},
{"bgm_volume", "BGM", true, "bgmSlider"},
} }
local optioncount = #ConfigScene.options local optioncount = #ConfigScene.options
@@ -26,10 +31,15 @@ function ConfigScene:new()
details = "In menus", details = "In menus",
state = "Changing game settings", state = "Changing game settings",
}) })
self.sfxSlider = newSlider(165, 375, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20})
self.bgmSlider = newSlider(465, 375, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20})
end end
function ConfigScene:update() function ConfigScene:update()
config["das_last_key"] = config.gamesettings.das_last_key == 2 config["das_last_key"] = config.gamesettings.das_last_key == 2
self.sfxSlider:update()
self.bgmSlider:update()
end end
function ConfigScene:render() function ConfigScene:render()
@@ -39,29 +49,45 @@ function ConfigScene:render()
0, 0, 0, 0, 0, 0,
0.5, 0.5 0.5, 0.5
) )
love.graphics.setFont(font_3x5_4) love.graphics.setFont(font_3x5_4)
love.graphics.print("GAME SETTINGS", 80, 40) love.graphics.print("GAME SETTINGS", 80, 40)
--Lazy check to see if we're on the SFX or BGM slider. Probably will need to be rewritten if more options get added.
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22) if not ConfigScene.options[self.highlight][3] then
love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
else
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 322, 215, 33)
end
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
for i, option in ipairs(ConfigScene.options) do for i, option in ipairs(ConfigScene.options) do
love.graphics.setColor(1, 1, 1, 1) if not option[3] then
love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left") love.graphics.setColor(1, 1, 1, 1)
for j, setting in ipairs(option[3]) do love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left")
love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5) for j, setting in ipairs(option[4]) do
love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center") 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
end end
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_3)
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 325)
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 325)
love.graphics.setColor(1, 1, 1, 0.75)
self.sfxSlider:draw()
self.bgmSlider:draw()
end end
function ConfigScene:onInputPress(e) function ConfigScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" then if e.input == "menu_decide" or e.scancode == "return" then
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = TitleScene() scene = SettingsScene()
elseif e.input == "up" or e.scancode == "up" then elseif e.input == "up" or e.scancode == "up" then
playSE("cursor") playSE("cursor")
self.highlight = Mod1(self.highlight-1, optioncount) self.highlight = Mod1(self.highlight-1, optioncount)
@@ -69,16 +95,28 @@ function ConfigScene:onInputPress(e)
playSE("cursor") playSE("cursor")
self.highlight = Mod1(self.highlight+1, optioncount) self.highlight = Mod1(self.highlight+1, optioncount)
elseif e.input == "left" or e.scancode == "left" then elseif e.input == "left" or e.scancode == "left" then
playSE("cursor_lr") if not self.options[self.highlight][3] then
local option = ConfigScene.options[self.highlight] playSE("cursor_lr")
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3]) 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)))
end
elseif e.input == "right" or e.scancode == "right" then elseif e.input == "right" or e.scancode == "right" then
playSE("cursor_lr") if not self.options[self.highlight][3] then
local option = ConfigScene.options[self.highlight] playSE("cursor_lr")
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3]) 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))
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()
scene = TitleScene() scene = SettingsScene()
end end
end end

View File

@@ -18,6 +18,7 @@ local configurable_inputs = {
"rotate_180", "rotate_180",
"hold", "hold",
"retry", "retry",
"pause",
} }
local function newSetInputs() local function newSetInputs()
@@ -82,7 +83,7 @@ function ConfigScene:onInputPress(e)
-- function keys, escape, and tab are reserved and can't be remapped -- function keys, escape, and tab are reserved and can't be remapped
if e.scancode == "escape" and config.input then if e.scancode == "escape" and config.input then
-- cancel only if there was an input config already -- cancel only if there was an input config already
scene = TitleScene() scene = SettingsScene()
elseif self.input_state > table.getn(configurable_inputs) then elseif self.input_state > table.getn(configurable_inputs) then
if e.scancode == "return" then if e.scancode == "return" then
-- save new input, then load next scene -- save new input, then load next scene

View File

@@ -11,6 +11,14 @@ function ModeSelectScene:new()
ruleset = current_ruleset, ruleset = current_ruleset,
select = "mode", select = "mode",
} }
self.secret_inputs = {
rotate_left = false,
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",
@@ -18,6 +26,7 @@ function ModeSelectScene:new()
end end
function ModeSelectScene:update() function ModeSelectScene:update()
switchBGM(nil) -- experimental
end end
function ModeSelectScene:render() function ModeSelectScene:render()
@@ -66,7 +75,7 @@ function ModeSelectScene:onInputPress(e)
config.current_ruleset = current_ruleset config.current_ruleset = current_ruleset
playSE("mode_decide") playSE("mode_decide")
saveConfig() saveConfig()
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset]) scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset], self.secret_inputs)
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") playSE("cursor")
@@ -78,6 +87,14 @@ function ModeSelectScene:onInputPress(e)
playSE("cursor_lr") 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
self.secret_inputs[e.input] = true
end
end
function ModeSelectScene:onInputRelease(e)
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then
self.secret_inputs[e.input] = false
end end
end end

65
scene/settings.lua Normal file
View File

@@ -0,0 +1,65 @@
local SettingsScene = Scene:extend()
SettingsScene.title = "Settings"
local menu_screens = {
InputConfigScene,
GameConfigScene,
TuningScene
}
function SettingsScene:new()
self.menu_state = 1
DiscordRPC:update({
details = "In menus",
state = "Changing settings",
})
end
function SettingsScene:update() end
function SettingsScene: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("SETTINGS", 80, 40)
love.graphics.setFont(font_3x5_2)
love.graphics.print("Here, you can change some settings that change\nthe look and feel of the game.", 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
function SettingsScene:changeOption(rel)
local len = table.getn(menu_screens)
self.menu_state = (self.menu_state + len + rel - 1) % len + 1
end
function SettingsScene:onInputPress(e)
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 e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
scene = TitleScene()
end
end
return SettingsScene

View File

@@ -1,9 +1,12 @@
local TitleScene = Scene:extend() local TitleScene = Scene:extend()
TitleScene.title = "Title"
TitleScene.restart_message = false
local main_menu_screens = { local main_menu_screens = {
ModeSelectScene, ModeSelectScene,
InputConfigScene, SettingsScene,
GameConfigScene, CreditsScene,
ExitScene, ExitScene,
} }
@@ -24,6 +27,11 @@ local mainmenuidle = {
function TitleScene:new() function TitleScene:new()
self.main_menu_state = 1 self.main_menu_state = 1
self.frames = 0
self.snow_bg_opacity = 0
self.y_offset = 0
self.text = ""
self.text_flag = false
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = mainmenuidle[math.random(#mainmenuidle)], state = mainmenuidle[math.random(#mainmenuidle)],
@@ -31,17 +39,42 @@ function TitleScene:new()
end end
function TitleScene:update() function TitleScene:update()
if self.text_flag then
self.frames = self.frames + 1
self.snow_bg_opacity = self.snow_bg_opacity + 0.01
end
if self.frames < 125 then self.y_offset = self.frames
elseif self.frames < 185 then self.y_offset = 125
else self.y_offset = 310 - self.frames end
end end
function TitleScene:render() function TitleScene:render()
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
love.graphics.setColor(1, 1, 1, 1 - self.snow_bg_opacity)
love.graphics.draw( love.graphics.draw(
backgrounds["title"], backgrounds["title"],
0, 0, 0, 0, 0, 0,
0.5, 0.5 0.5, 0.5
) )
love.graphics.setColor(1, 1, 1, self.snow_bg_opacity)
love.graphics.draw(
backgrounds["snow"],
0, 0, 0,
0.5, 0.5
)
love.graphics.draw(
misc_graphics["santa"],
400, -205 + self.y_offset,
0, 0.5, 0.5
)
love.graphics.print("Happy Holidays!", 320, -100 + self.y_offset)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print(self.restart_message and "Restart Cambridge..." or "", 0, 0)
love.graphics.setColor(1, 1, 1, 0.5) love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 20, 278 + 20 * self.main_menu_state, 160, 22) love.graphics.rectangle("fill", 20, 278 + 20 * self.main_menu_state, 160, 22)
@@ -69,6 +102,11 @@ function TitleScene:onInputPress(e)
playSE("cursor") playSE("cursor")
elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
love.event.quit() love.event.quit()
else
self.text = self.text .. (e.scancode ~= nil and e.scancode or "")
if self.text == "ffffff" then
self.text_flag = true
end
end end
end end

85
scene/tuning.lua Normal file
View File

@@ -0,0 +1,85 @@
local TuningScene = Scene:extend()
TuningScene.title = "Tuning Settings"
require 'load.save'
require 'libs.simple-slider'
TuningScene.options = {
-- Serves as a reference for the options available in the menu. Format: {name in config, name as displayed if applicable, slider name}
{"das", "DAS", "dasSlider"},
{"arr", "ARR", "arrSlider"},
}
local optioncount = #TuningScene.options
function TuningScene:new()
DiscordRPC:update({
details = "In menus",
state = "Changing tuning settings",
})
self.highlight = 1
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20})
self.arrSlider = newSlider(290, 325, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20})
end
function TuningScene:update()
self.dasSlider:update()
self.arrSlider:update()
end
function TuningScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(
backgrounds["game_config"],
0, 0, 0,
0.5, 0.5
)
love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 75, 73 + self.highlight * 100, 400, 33)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_4)
love.graphics.print("TUNING SETTINGS", 80, 40)
love.graphics.setFont(font_3x5_2)
love.graphics.print("These settings will only apply to modes\nthat do not use their own tunings.", 80, 90)
love.graphics.setFont(font_3x5_3)
love.graphics.print("Delayed Auto-Shift (DAS): " .. math.floor(self.dasSlider:getValue()) .. "F", 80, 175)
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 275)
love.graphics.setColor(1, 1, 1, 0.75)
self.dasSlider:draw()
self.arrSlider:draw()
end
function TuningScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" then
playSE("mode_decide")
saveConfig()
scene = SettingsScene()
elseif e.input == "up" or e.scancode == "up" then
playSE("cursor")
self.highlight = Mod1(self.highlight-1, optioncount)
elseif e.input == "down" or e.scancode == "down" then
playSE("cursor")
self.highlight = Mod1(self.highlight+1, optioncount)
elseif e.input == "left" or e.scancode == "left" then
playSE("cursor")
sld = self[self.options[self.highlight][3]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 1) / (sld.max - sld.min)))
elseif e.input == "right" or e.scancode == "right" then
playSE("cursor")
sld = self[self.options[self.highlight][3]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 1) / (sld.max - sld.min)))
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave()
scene = SettingsScene()
end
end
return TuningScene

View File

@@ -114,6 +114,7 @@ function Grid:markClearedRows()
skin = self.grid[row][x].skin, skin = self.grid[row][x].skin,
colour = "X" colour = "X"
} }
self.grid_age[row][x] = 0
end end
end end
end end
@@ -194,7 +195,7 @@ function Grid:applyPiece(piece)
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
x = piece.position.x + offset.x x = piece.position.x + offset.x
y = piece.position.y + offset.y y = piece.position.y + offset.y
if y + 1 > 0 then if y + 1 > 0 and y < 24 then
self.grid[y+1][x+1] = { self.grid[y+1][x+1] = {
skin = piece.skin, skin = piece.skin,
colour = piece.colour colour = piece.colour
@@ -273,6 +274,43 @@ function Grid:checkSecretGrade()
return sgrade return sgrade
end end
function Grid:hasGemBlocks()
for y = 1, 24 do
for x = 1, 10 do
if self.grid[y][x].skin == "gem" then
return true
end
end
end
return false
end
function Grid:mirror()
local new_grid = {}
for y = 1, 24 do
new_grid[y] = {}
for x = 1, 10 do
new_grid[y][x] = empty
end
end
for y = 1, 24 do
for x = 1, 10 do
new_grid[y][x] = self.grid[y][11 - x]
end
end
self.grid = new_grid
end
function Grid:applyMap(map)
for y, row in pairs(map) do
for x, block in pairs(row) do
self.grid_age[y][x] = 0
self.grid[y][x] = block
end
end
end
function Grid:update() function Grid:update()
for y = 1, 24 do for y = 1, 24 do
for x = 1, 10 do for x = 1, 10 do
@@ -293,18 +331,21 @@ function Grid:draw()
else else
if self.grid[y][x].skin == "bone" then if self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
else 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) love.graphics.setColor(0.5, 0.5, 0.5, 1)
end end
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)
end end
if self.grid[y][x].skin ~= "bone" 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 then if y > 1 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 < 24 and self.grid[y+1][x] == empty then if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or 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
@@ -322,13 +363,18 @@ end
function Grid:drawOutline() function Grid:drawOutline()
for y = 5, 24 do for y = 5, 24 do
for x = 1, 10 do for x = 1, 10 do
if self.grid[y][x] ~= empty then 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.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 then if y > 1 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 < 24 and self.grid[y+1][x] == empty then if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or 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
@@ -349,7 +395,7 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
for x = 1, 10 do for x = 1, 10 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 opacity = 1 - self.grid_age[y][x] / 15
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
@@ -361,10 +407,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 then if y > 1 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 < 24 and self.grid[y+1][x] == empty then if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or 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
@@ -380,4 +427,45 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
end end
end end
function Grid:drawCustom(colour_function, gamestate)
--[[
colour_function: (game, block, x, y, age) -> (R, G, B, A, outlineA)
When called, calls the supplied function on every block passing the block itself as argument
as well as coordinates and the grid_age value of the same cell.
Should return a RGBA colour for the block, as well as the opacity of the stack outline (0 for no outline).
gamestate: the gamemode instance itself to pass in colour_function
]]
for y = 5, 24 do
for x = 1, 10 do
local block = self.grid[y][x]
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
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
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end
if y < 24 and self.grid[y+1][x] == empty or
(y + 1 > 24 or 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
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end
end
end
end
end
end
return Grid return Grid

View File

@@ -143,9 +143,10 @@ function Piece:draw(opacity, brightness, grid, partial_das)
love.graphics.setColor(brightness, brightness, brightness, opacity) love.graphics.setColor(brightness, brightness, brightness, opacity)
local offsets = self:getBlockOffsets() local offsets = self:getBlockOffsets()
local gravity_offset = 0 local gravity_offset = 0
--if grid ~= nil and not self:isDropBlocked(grid) then if config.gamesettings.smooth_movement == 1 and
-- gravity_offset = self.gravity * 16 grid ~= nil and not self:isDropBlocked(grid) then
--end gravity_offset = self.gravity * 16
end
if partial_das == nil then partial_das = 0 end if partial_das == nil then partial_das = 0 end
for index, offset in pairs(offsets) do for index, offset in pairs(offsets) do
local x = self.position.x + offset.x local x = self.position.x + offset.x

View File

@@ -5,13 +5,14 @@ local playedReadySE = false
local playedGoSE = false local playedGoSE = false
local Grid = require 'tetris.components.grid' local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer' local Randomizer = require 'tetris.randomizers.bag7'
local BagRandomizer = require 'tetris.randomizers.bag'
local GameMode = Object:extend() local GameMode = Object:extend()
GameMode.rollOpacityFunction = function(age) return 0 end GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new() function GameMode:new(secret_inputs)
self.grid = Grid() self.grid = Grid()
self.randomizer = Randomizer() self.randomizer = Randomizer()
self.piece = nil self.piece = nil
@@ -56,6 +57,7 @@ function GameMode:new()
self.hard_drop_locked = false self.hard_drop_locked = false
self.lock_on_soft_drop = false self.lock_on_soft_drop = false
self.lock_on_hard_drop = false self.lock_on_hard_drop = false
self.used_randomizer = nil
self.hold_queue = nil self.hold_queue = nil
self.held = false self.held = false
self.section_start_time = 0 self.section_start_time = 0
@@ -72,17 +74,29 @@ 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 = self:getSkin(),
shape = self.randomizer:nextPiece(), shape = self.used_randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(), orientation = ruleset:getDefaultOrientation(),
} }
end end
function GameMode:initialize(ruleset) function GameMode:getSkin()
return "2tie"
end
function GameMode:initialize(ruleset, secret_inputs)
-- generate next queue -- generate next queue
self:new() self:new(secret_inputs)
self.used_randomizer = (
ruleset.pieces == self.randomizer.possible_pieces and
self.randomizer or
(
ruleset.pieces == 7 and
Randomizer() or
BagRandomizer(ruleset.pieces)
)
)
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
@@ -162,6 +176,7 @@ function GameMode:update(inputs, ruleset)
not self.hard_drop_locked then not self.hard_drop_locked then
self:onHardDrop(piece_dy) self:onHardDrop(piece_dy)
if self.lock_on_hard_drop then if self.lock_on_hard_drop then
self.piece_hard_dropped = true
self.piece.locked = true self.piece.locked = true
end end
end end
@@ -193,7 +208,9 @@ function GameMode:update(inputs, ruleset)
if cleared_row_count > 0 then if cleared_row_count > 0 then
playSE("erase") playSE("erase")
self.lcd = self:getLineClearDelay() self.lcd = self:getLineClearDelay()
self.are = self:getLineARE() self.are = (
ruleset.are and self:getLineARE() or 0
)
if self.lcd == 0 then if self.lcd == 0 then
self.grid:clearClearedRows() self.grid:clearClearedRows()
if self.are == 0 then if self.are == 0 then
@@ -202,7 +219,7 @@ function GameMode:update(inputs, ruleset)
end end
self:onLineClear(cleared_row_count) self:onLineClear(cleared_row_count)
else else
if self:getARE() == 0 then if self:getARE() == 0 or not ruleset.are then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
else else
self.are = self:getARE() self.are = self:getARE()
@@ -311,12 +328,27 @@ function GameMode:chargeDAS(inputs)
end end
end end
function GameMode:areCancel(inputs, ruleset)
if ruleset.are_cancel and self.piece_hard_dropped and
not self.prev_inputs.up and
strTrueValues(inputs) ~= "" then
self.lcd = 0
self.are = 0
end
end
function GameMode:processDelays(inputs, ruleset, drop_speed) function GameMode:processDelays(inputs, ruleset, drop_speed)
if self.ready_frames == 100 then if self.ready_frames == 100 then
playedReadySE = false playedReadySE = false
playedGoSE = false playedGoSE = false
end end
if self.ready_frames > 0 then if self.ready_frames > 0 then
if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
self.buffer_hard_drop = true
end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
if not playedReadySE then if not playedReadySE then
playedReadySE = true playedReadySE = true
playSEOnce("ready") playSEOnce("ready")
@@ -330,19 +362,14 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
elseif self.lcd > 0 then elseif self.lcd > 0 then
self.lcd = self.lcd - 1 if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
if ruleset.are_cancel and self.buffer_hard_drop = true
(self.move == "none" and not self.prev_inputs["up"] and
not self.prev_inputs["rotate_left"] and not self.prev_inputs["rotate_left2"] and
not self.prev_inputs["rotate_right"] and not self.prev_inputs["rotate_right2"] and
not self.prev_inputs["rotate_180"]) and
(inputs["left"] or inputs["right"] or inputs["up"] or
inputs["rotate_left"] or inputs["rotate_left2"] or
inputs["rotate_right"] or inputs["rotate_right2"] or
inputs["rotate_180"]) then
self.lcd = 0
self.are = 0
end end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
self.lcd = self.lcd - 1
self:areCancel(inputs, ruleset)
if self.lcd == 0 then if self.lcd == 0 then
self.grid:clearClearedRows() self.grid:clearClearedRows()
playSE("fall") playSE("fall")
@@ -351,18 +378,14 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end end
end end
elseif self.are > 0 then elseif self.are > 0 then
self.are = self.are - 1 if not self.prev_inputs["up"] and inputs["up"] and self.enable_hard_drop then
if ruleset.are_cancel and self.buffer_hard_drop = true
(self.move == "none" and not self.prev_inputs["up"] and
not self.prev_inputs["rotate_left"] and not self.prev_inputs["rotate_left2"] and
not self.prev_inputs["rotate_right"] and not self.prev_inputs["rotate_right2"] and
not self.prev_inputs["rotate_180"]) and
(inputs["left"] or inputs["right"] or inputs["up"] or
inputs["rotate_left"] or inputs["rotate_left2"] or
inputs["rotate_right"] or inputs["rotate_right2"] or
inputs["rotate_180"]) then
self.are = 0
end end
if not self.prev_inputs["down"] and inputs["down"] then
self.buffer_soft_drop = true
end
self.are = self.are - 1
self:areCancel(inputs, ruleset)
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
@@ -407,14 +430,31 @@ function GameMode:hold(inputs, ruleset, ihs)
end end
function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next_piece) function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next_piece)
self.piece_hard_dropped = false
local gravity = self:getGravity() local gravity = self:getGravity()
self.piece = ruleset:initializePiece( self.piece = ruleset:initializePiece(
inputs, piece_data, self.grid, gravity, inputs, piece_data, self.grid, gravity,
self.prev_inputs, self.move, self.prev_inputs, self.move,
self:getLockDelay(), self:getDropSpeed(), self:getLockDelay(), self:getDropSpeed(),
self.lock_drop, self.lock_hard_drop, self.big_mode, self.lock_drop, self.lock_hard_drop, self.big_mode,
self.irs self.irs, self.buffer_hard_drop, self.buffer_soft_drop,
self.lock_on_hard_drop, self.lock_on_soft_drop
) )
if self.piece:isDropBlocked(self.grid) and
self.grid:canPlacePiece(self.piece) then
playSE("bottom")
end
if self.buffer_hard_drop then
self.buffer_hard_drop = false
self:onHardDrop(self.piece.position.y - (
self.big_mode and
ruleset.big_spawn_positions[self.piece.shape].y or
ruleset.spawn_positions[self.piece.shape].y)
)
end
if self.buffer_soft_drop then
self.buffer_soft_drop = false
end
if self.lock_drop then if self.lock_drop then
self.drop_locked = true self.drop_locked = true
end end
@@ -425,11 +465,11 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
table.remove(self.next_queue, 1) table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset)) table.insert(self.next_queue, self:getNextPiece(ruleset))
end end
if config.gamesettings.next_se == 1 then self:playNextSound() end self:playNextSound(ruleset)
end end
function GameMode:playNextSound() function GameMode:playNextSound(ruleset)
playSE("blocks", self.next_queue[1].shape) playSE("blocks", ruleset.next_sounds[self.next_queue[1].shape])
end end
function GameMode:getHighScoreData() function GameMode:getHighScoreData()

View File

@@ -27,7 +27,7 @@ function MarathonA3Game:new()
self.section_cool_grade = 0 self.section_cool_grade = 0
self.section_status = { [0] = "none" } self.section_status = { [0] = "none" }
self.section_start_time = 0 self.section_start_time = 0
self.section_70_times = { [0] = 0 } self.secondary_section_times = { [0] = 0 }
self.section_times = { [0] = 0 } self.section_times = { [0] = 0 }
self.section_cool = false self.section_cool = false
@@ -163,8 +163,8 @@ function MarathonA3Game:onLineClear(cleared_row_count)
self:updateSectionTimes(self.level, self.level + advanced_levels) self:updateSectionTimes(self.level, self.level + advanced_levels)
if not self.clear then if not self.clear then
self.level = math.min(self.level + advanced_levels, 999) self.level = math.min(self.level + advanced_levels, 999)
self.speed_level = self.speed_level + advanced_levels
end end
self.speed_level = self.speed_level + advanced_levels
if self.level == 999 and not self.clear then if self.level == 999 and not self.clear then
self.clear = true self.clear = true
self.grid:clear() self.grid:clear()
@@ -194,7 +194,7 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
-- record new section -- record new section
section_time = self.frames - self.section_start_time section_time = self.frames - self.section_start_time
table.insert(self.section_times, section_time) table.insert(self.section_times, section_time)
self.section_start_time = self.frames if new_level < 999 then self.section_start_time = self.frames end
self.speed_level = self.section_cool and self.speed_level + 100 or self.speed_level self.speed_level = self.section_cool and self.speed_level + 100 or self.speed_level
@@ -214,15 +214,15 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
elseif old_level % 100 < 70 and new_level % 100 >= 70 then elseif old_level % 100 < 70 and new_level % 100 >= 70 then
-- record section 70 time -- record section 70 time
section_70_time = self.frames - self.section_start_time section_70_time = self.frames - self.section_start_time
table.insert(self.section_70_times, section_70_time) table.insert(self.secondary_section_times, section_70_time)
if section <= 9 and self.section_status[section - 1] == "cool" and if section <= 9 and self.section_status[section - 1] == "cool" and
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 then
self.section_cool = true self.section_cool = true
self.coolregret_message = "COOL!!" self.coolregret_message = "COOL!!"
self.coolregret_timer = 300 self.coolregret_timer = 300
elseif self.section_status[section - 1] == "cool" then self.section_cool = false elseif self.section_status[section - 1] == "cool" then self.section_cool = false
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then elseif section <= 9 and self.secondary_section_times[section] < cool_cutoffs[section] then
self.section_cool = true self.section_cool = true
self.coolregret_message = "COOL!!" self.coolregret_message = "COOL!!"
self.coolregret_timer = 300 self.coolregret_timer = 300
@@ -395,6 +395,16 @@ MarathonA3Game.mRollOpacityFunction = function(age)
else return 1 - age / 4 end else return 1 - age / 4 end
end end
function MarathonA3Game:sectionColourFunction(section)
if self.section_status[section] == "cool" then
return { 0, 1, 0, 1 }
elseif self.section_status[section] == "regret" then
return { 1, 0, 0, 1 }
else
return { 1, 1, 1, 1 }
end
end
function MarathonA3Game:drawScoringInfo() function MarathonA3Game:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
@@ -415,6 +425,8 @@ function MarathonA3Game:drawScoringInfo()
-- draw section time data -- draw section time data
current_section = math.floor(self.level / 100) + 1 current_section = math.floor(self.level / 100) + 1
self:drawSectionTimesWithSecondary(current_section)
--[[
section_x = 530 section_x = 530
section_70_x = 440 section_70_x = 440
@@ -425,21 +437,22 @@ function MarathonA3Game:drawScoringInfo()
end end
end end
for section, time in pairs(self.section_70_times) do for section, time in pairs(self.secondary_section_times) do
if section > 0 then if section > 0 then
love.graphics.printf(formatTime(time), section_70_x, 40 + 20 * section, 90, "left") love.graphics.printf(formatTime(time), section_70_x, 40 + 20 * section, 90, "left")
end end
end end
local current_x local current_x
if table.getn(self.section_times) < table.getn(self.section_70_times) then if table.getn(self.section_times) < table.getn(self.secondary_section_times) then
current_x = section_x current_x = section_x
else else
current_x = section_70_x current_x = section_70_x
end end
if not self.clear then love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left") end if not self.clear then love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left") end
]]--
if(self.coolregret_timer > 0) then if(self.coolregret_timer > 0) then
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center") love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
self.coolregret_timer = self.coolregret_timer - 1 self.coolregret_timer = self.coolregret_timer - 1
@@ -475,7 +488,7 @@ function MarathonA3Game:getSectionEndLevel()
end end
function MarathonA3Game:getBackground() function MarathonA3Game:getBackground()
return math.floor(self.level / 100) return math.floor(self.speed_level / 100)
end end
return MarathonA3Game return MarathonA3Game

View File

@@ -86,12 +86,8 @@ function PhantomMania2Game:getGarbageLimit()
else return 8 end else return 8 end
end end
function PhantomMania2Game:getNextPiece(ruleset) function PhantomMania2Game:getSkin()
return { return self.level >= 1000 and "bone" or "2tie"
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end end
function PhantomMania2Game:hitTorikan(old_level, new_level) function PhantomMania2Game:hitTorikan(old_level, new_level)

583
tetris/modes/sakura.lua Normal file
View File

@@ -0,0 +1,583 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local SakuraRandomizer = require 'tetris.randomizers.sakura'
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
local SakuraGame = GameMode:extend()
SakuraGame.name = "Sakura A3"
SakuraGame.hash = "SakuraA3"
SakuraGame.tagline = "Clear away the Gem Blocks as fast as possible!"
local b = {
["r"] = { skin = "2tie", colour = "R" },
["o"] = { skin = "2tie", colour = "O" },
["y"] = { skin = "2tie", colour = "Y" },
["g"] = { skin = "2tie", colour = "G" },
["c"] = { skin = "2tie", colour = "C" },
["b"] = { skin = "2tie", colour = "B" },
["m"] = { skin = "2tie", colour = "M" },
["R"] = { skin = "gem", colour = "R" },
["O"] = { skin = "gem", colour = "O" },
["Y"] = { skin = "gem", colour = "Y" },
["G"] = { skin = "gem", colour = "G" },
["C"] = { skin = "gem", colour = "C" },
["B"] = { skin = "gem", colour = "B" },
["M"] = { skin = "gem", colour = "M" },
}
local effects = {
[4] = "mirror",
[8] = "xray",
[12] = "color",
[13] = "mirror",
[16] = "roll",
[23] = "big"
}
local maps = {
[1] = {
[22] = {nil, nil, b.O, b.R, nil, nil, b.M, b.m, nil, nil},
[23] = {nil, b.G, b.c, b.c, b.c, b.c, b.c, b.c, b.Y, nil},
[24] = {nil, b.C, b.y, b.y, b.y, b.y, b.y, b.y, b.B, nil},
},
[2] = {
[20] = {nil, nil, nil, nil, b.G, b.b, b.b, b.M, nil, nil},
[21] = {nil, nil, nil, nil, b.c, b.c, b.c, b.c, nil, nil},
[22] = {nil, nil, nil, nil, nil, b.R, b.Y, b.O, nil, nil},
[23] = {nil, b.B, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil},
[24] = {nil, b.b, b.b, b.b, b.b, b.b, b.b, b.b, b.C, nil},
},
[3] = {
[20] = {nil, nil, nil, b.R, b.m, b.o, b.M, nil, nil, nil},
[21] = {nil, nil, nil, nil, b.o, b.O, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
[23] = {nil, b.m, b.o, b.m, b.o, b.m, b.o, b.m, b.o, nil},
[24] = {nil, b.B, b.m, b.o, b.m, b.o, b.m, b.o, b.C, nil},
},
[4] = {
[21] = {nil, nil, b.O, b.g, b.g, b.g, b.g, nil, nil, nil},
[22] = {nil, nil, nil, b.R, b.M, b.b, b.b, nil, nil, nil},
[23] = {b.G, nil, b.Y, b.g, b.g, b.g, b.g, b.g, nil, nil},
[24] = {b.b, b.C, b.b, b.b, b.b, b.b, b.b, b.b, b.B, nil},
},
[5] = {
[16] = {nil, b.B, b.c, b.y, b.c, b.G, b.c, b.y, b.C, nil},
[22] = {nil, nil, b.c, b.y, b.c, b.y, b.c, b.y, nil, nil},
[23] = {nil, b.O, b.y, b.c, b.y, b.c, b.y, b.c, b.Y, nil},
[24] = {nil, b.R, b.c, b.y, b.c, b.y, b.c, b.y, b.M, nil},
},
[6] = {
[21] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
[22] = {nil, nil, b.R, nil, b.b, b.y, nil, b.M, nil, nil},
[23] = {nil, nil, nil, nil, b.y, b.b, nil, nil, nil, nil},
[24] = {nil, b.G, b.y, b.b, b.C, b.y, b.b, b.y, b.B, nil},
},
[7] = {
[20] = {nil, b.C, b.G, nil, b.r, b.g, b.r, b.g, nil, nil},
[21] = {nil, nil, nil, nil, b.R, b.M, b.g, b.r, nil, nil},
[22] = {b.r, nil, nil, nil, b.r, b.g, b.O, b.Y, nil, nil},
[23] = {b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.r, nil, nil},
[24] = {b.r, b.g, b.r, b.g, b.r, b.g, b.r, b.g, b.B, nil},
},
[8] = {
[15] = {nil, nil, nil, b.B, b.m, b.m, b.m, b.m, b.m, b.C},
[16] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
[17] = {nil, nil, nil, nil, nil, nil, nil, nil, nil, b.m},
[18] = {nil, b.Y, b.y, b.y, b.y, b.y, b.y, b.y, b.y, b.G},
[21] = {b.b, b.b, b.b, b.b, b.b, b.b, b.O, nil, nil, nil},
[22] = {b.b, nil, nil, nil, nil, nil, nil, nil, nil, nil},
[23] = {b.M, nil, nil, nil, nil, nil, nil, nil, nil, nil},
[24] = {b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.o, b.R, nil},
},
[9] = {
[18] = {nil, nil, nil, b.Y, b.m, b.m, b.m, b.m, nil, nil},
[19] = {nil, nil, nil, b.c, b.c, b.c, b.c, b.G, nil, nil},
[20] = {b.m, b.m, b.m, b.O, b.M, b.R, nil, nil, nil, nil},
[21] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, nil, nil},
[22] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, nil, nil},
[23] = {b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.c, b.C, nil},
[24] = {b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.m, b.B, nil},
},
[10] = {
[18] = {nil, nil, nil, b.C, b.g, b.g, b.B, nil, nil, nil},
[19] = {nil, nil, b.G, b.g, b.g, b.g, b.g, b.Y, nil, nil},
[20] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.g, b.O, nil},
[21] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.c, nil, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.c, nil, b.o, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.R, b.o, b.o, nil, nil, nil},
},
[11] = {
[18] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[19] = {nil, nil, nil, nil, b.R, nil, nil, nil, nil, nil},
[20] = {nil, nil, nil, nil, b.r, b.O, nil, nil, nil, nil},
[21] = {nil, nil, nil, nil, nil, b.M, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.G, b.Y, nil, nil, nil, nil},
[24] = {b.C, b.g, b.g, nil, b.o, b.o, nil, b.g, b.g, b.B},
},
[12] = {
[21] = {nil, nil, nil, nil, nil, nil, nil, b.g, b.g, b.Y},
[22] = {nil, nil, b.r, b.G, b.r, nil, nil, nil, b.O, b.g},
[23] = {nil, b.r, b.C, b.r, b.B, b.r, nil, nil, nil, b.M},
[24] = {b.r, b.r, b.r, b.R, b.r, b.r, b.r, nil, nil, nil},
},
[13] = {
[20] = {b.c, nil, nil, nil, nil, nil, nil, nil, nil, b.B},
[21] = {b.c, b.c, nil, nil, nil, nil, nil, nil, b.C, b.c},
[22] = {b.c, b.c, b.c, nil, nil, nil, nil, b.G, b.c, b.c},
[23] = {b.b, b.b, b.b, b.b, nil, nil, b.Y, b.b, b.b, b.b},
[24] = {nil, b.M, b.b, b.b, b.b, b.O, b.b, b.b, b.R, nil},
},
[14] = {
[20] = {nil, nil, nil, b.y, b.r, b.y, nil, nil, nil, nil},
[21] = {b.R, nil, nil, b.Y, b.y, b.r, b.G, nil, nil, nil},
[22] = {nil, nil, nil, b.y, b.r, b.y, b.r, b.y, b.r, b.B},
[23] = {nil, nil, nil, nil, nil, nil, nil, b.O, b.y, b.r},
[24] = {nil, nil, nil, nil, nil, nil, b.M, b.y, b.r, b.C},
},
[15] = {
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[18] = {nil, nil, b.b, b.y, b.b, b.b, b.y, b.b, nil, nil},
[19] = {nil, nil, nil, b.y, b.b, b.b, b.y, nil, nil, nil},
[20] = {nil, nil, nil, nil, b.O, b.Y, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.M, b.R, nil, nil, nil, nil},
[23] = {nil, nil, nil, b.G, b.y, b.y, b.C, nil, nil, nil},
[24] = {nil, nil, b.B, b.y, b.y, b.y, b.y, b.y, nil, nil},
},
[16] = {
[18] = {nil, nil, b.O, nil, nil, nil, nil, b.B, nil, nil},
[19] = {nil, nil, b.c, nil, nil, b.G, nil, b.c, nil, nil},
[20] = {nil, nil, b.c, nil, b.C, b.R, nil, b.c, nil, nil},
[21] = {nil, nil, b.c, nil, nil, nil, nil, b.c, nil, nil},
[22] = {nil, nil, b.Y, b.c, b.c, b.c, b.c, b.M, nil, nil},
},
[17] = {
[15] = {b.O, nil, nil, b.g, nil, nil, b.m, nil, nil, b.Y},
[16] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[17] = {nil, nil, nil, b.g, nil, nil, b.R, nil, nil, nil},
[18] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[19] = {nil, nil, nil, b.M, nil, nil, b.m, nil, nil, nil},
[20] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[21] = {nil, nil, nil, b.g, nil, nil, b.G, nil, nil, nil},
[22] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[23] = {nil, nil, nil, b.g, nil, nil, b.m, nil, nil, nil},
[24] = {nil, b.m, b.m, b.m, b.B, b.C, b.g, b.g, b.g, nil},
},
[18] = {
[19] = {nil, nil, nil, b.y, b.B, b.y, b.y, nil, nil, nil},
[20] = {nil, nil, b.y, nil, nil, nil, nil, b.y, nil, nil},
[21] = {nil, b.o, nil, nil, b.C, b.R, nil, nil, b.O, nil},
[22] = {nil, b.M, nil, nil, b.Y, b.G, nil, nil, b.o, nil},
[23] = {nil, b.r, b.o, nil, nil, nil, nil, b.o, b.r, nil},
[24] = {nil, b.r, b.r, b.o, b.o, b.o, b.o, b.r, b.r, nil},
},
[19] = {
[15] = {nil, nil, nil, nil, nil, nil, b.O, nil, nil, nil},
[16] = {nil, nil, nil, nil, nil, b.o, nil, nil, nil, nil},
[17] = {nil, nil, nil, nil, b.o, b.r, b.o, nil, nil, nil},
[18] = {nil, b.o, nil, nil, b.o, b.r, b.r, b.o, nil, nil},
[19] = {nil, b.o, b.o, nil, nil, b.R, b.r, b.r, nil, nil},
[20] = {nil, nil, b.M, b.r, nil, b.r, b.r, b.r, b.r, nil},
[21] = {nil, nil, b.r, b.r, b.r, b.r, b.y, b.G, b.o, b.o},
[22] = {nil, b.r, b.o, b.y, b.Y, b.y, b.y, b.y, b.y, b.o},
[23] = {nil, b.o, b.o, b.y, b.y, b.c, b.c, b.c, b.c, b.C},
[24] = {nil, nil, b.o, b.y, b.b, b.b, b.B, b.b, b.b, b.b},
},
[20] = {
[20] = {nil, nil, b.B, b.b, b.b, b.b, b.b, b.b, nil, nil},
[21] = {b.c, nil, nil, b.C, b.c, b.c, b.c, nil, nil, b.c},
[22] = {b.g, b.g, nil, nil, b.G, b.g, nil, nil, b.g, b.g},
[23] = {b.y, b.y, b.o, nil, nil, nil, nil, b.Y, b.y, b.y},
[24] = {b.r, b.r, b.r, b.R, nil, nil, b.M, b.r, b.r, b.r},
},
[21] = {
[16] = {nil, nil, b.g, b.g, b.g, b.g, b.g, nil, nil, nil},
[17] = {nil, b.g, b.g, b.g, b.g, b.g, b.g, b.B, b.g, nil},
[18] = {b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.g, b.C, nil},
[19] = {b.g, nil, nil, b.g, b.o, b.o, b.g, nil, nil, b.g},
[20] = {nil, b.R, nil, b.g, b.o, b.o, nil, b.M, nil, b.G},
[21] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[22] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.Y, b.O, nil, nil, nil, nil},
},
[22] = {
[15] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[16] = {nil, b.b, b.O, b.b, nil, nil, b.b, b.Y, b.b, nil},
[17] = {nil, nil, b.b, nil, nil, nil, nil, b.b, nil, nil},
[20] = {nil, nil, nil, nil, b.R, b.M, nil, nil, nil, nil},
[23] = {nil, nil, nil, b.b, b.C, b.G, b.b, nil, nil, nil},
[24] = {nil, nil, nil, b.b, b.b, b.B, b.b, nil, nil, nil},
},
[23] = {
[13] = {nil, nil, nil, nil, nil, nil, nil, nil, b.c, b.m},
[14] = {nil, nil, nil, nil, nil, nil, nil, nil, b.y, b.g},
[15] = {b.G, b.B, nil, nil, nil, nil, nil, nil, nil, nil},
[16] = {b.r, b.O, nil, nil, nil, nil, nil, nil, nil, nil},
[23] = {nil, nil, nil, nil, b.C, b.M, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, b.R, b.Y, nil, nil, nil, nil},
},
[24] = {
[20] = {b.g, b.g, b.g, b.g, b.G, nil, nil, nil, nil, nil},
[21] = {nil, nil, nil, nil, nil, b.O, b.y, b.y, b.y, b.Y},
[23] = {b.M, b.r, b.r, b.r, b.R, nil, nil, nil, nil, nil},
[24] = {nil, nil, nil, nil, nil, b.M, b.r, b.r, b.r, b.R},
},
[25] = {
[18] = {nil, nil, nil, nil, nil, b.B, nil, nil, nil, nil},
[19] = {nil, nil, nil, b.G, nil, nil, nil, b.C, nil, nil},
[20] = {nil, nil, nil, nil, nil, b.Y, nil, nil, nil, nil},
[21] = {nil, nil, nil, b.M, nil, nil, nil, b.O, nil, nil},
[22] = {nil, nil, nil, nil, nil, b.R, nil, nil, nil, nil},
},
[26] = {
[13] = {nil, nil, nil, nil, b.r, b.r, nil, nil, nil, nil},
[14] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[15] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[16] = {nil, nil, nil, nil, b.o, b.o, nil, nil, nil, nil},
[17] = {nil, nil, nil, b.O, b.o, b.o, b.Y, nil, nil, nil},
[18] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
[19] = {nil, nil, b.o, b.c, b.c, b.c, b.c, b.o, nil, nil},
[20] = {nil, nil, b.o, nil, nil, b.G, nil, b.o, nil, nil},
[21] = {nil, nil, b.o, nil, b.M, b.R, nil, b.o, nil, nil},
[22] = {nil, nil, b.o, b.b, b.b, b.b, b.B, b.o, nil, nil},
[23] = {nil, nil, b.o, b.C, b.b, b.b, b.b, b.o, nil, nil},
[24] = {nil, nil, b.o, b.o, b.o, b.o, b.o, b.o, nil, nil},
},
[27] = {
[15] = {nil, b.C, b.o, b.g, b.g, b.g, b.g, b.g, b.B, nil},
[16] = {b.g, nil, b.y, b.o, b.g, b.g, b.g, b.g, nil, b.y},
[17] = {b.g, b.g, nil, b.y, b.o, b.g, b.g, nil, b.y, b.o},
[18] = {b.g, b.g, b.g, nil, b.y, b.o, nil, b.y, b.o, b.g},
[19] = {b.g, b.g, b.g, b.o, nil, b.G, b.y, b.o, b.g, b.g},
[20] = {b.g, b.g, b.o, b.o, b.Y, nil, b.o, b.g, b.g, b.g},
[21] = {b.g, b.o, b.y, nil, b.o, b.O, nil, b.g, b.g, b.g},
[22] = {b.o, b.y, nil, b.g, b.g, b.o, b.y, nil, b.g, b.g},
[23] = {b.y, nil, b.g, b.g, b.g, b.g, b.o, b.y, nil, b.g},
[24] = {nil, b.M, b.g, b.g, b.g, b.g, b.g, b.o, b.R, nil},
},
}
local STAGE_TRANSITION_TIME = 300
function SakuraGame:new(secret_inputs)
self.super:new()
self.randomizer = (
(
secret_inputs.rotate_left and secret_inputs.rotate_right
) and History6RollsRandomizer() or SakuraRandomizer()
)
self.current_map = 1
self.time_limit = 10800
self.cleared_frames = STAGE_TRANSITION_TIME
self.stage_frames = 0
self.time_extend = 0
self.maps_cleared = 0
self.map_20_time = 0
self.stage_pieces = 0
self.grid:applyMap(maps[self.current_map])
self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true
self.next_queue_length = 3
end
function SakuraGame:checkRequirements()
if self.maps_cleared >= 14 + 2 * (self.current_map - 20) and
self.map_20_time <= frameTime(8,00) - frameTime(0,30) * (self.current_map - 20)
then
return false
end
return true
end
function SakuraGame:getGravity()
if self.level < 8 then return 4/256
elseif self.level < 19 then return 5/256
elseif self.level < 35 then return 6/256
elseif self.level < 40 then return 8/256
elseif self.level < 50 then return 10/256
elseif self.level < 60 then return 12/256
elseif self.level < 70 then return 16/256
elseif self.level < 80 then return 32/256
elseif self.level < 90 then return 48/256
elseif self.level < 100 then return 64/256
elseif self.level < 108 then return 4/256
elseif self.level < 119 then return 5/256
elseif self.level < 125 then return 6/256
elseif self.level < 131 then return 8/256
elseif self.level < 139 then return 12/256
elseif self.level < 149 then return 32/256
elseif self.level < 156 then return 48/256
elseif self.level < 164 then return 80/256
elseif self.level < 174 then return 112/256
elseif self.level < 180 then return 128/256
elseif self.level < 200 then return 144/256
elseif self.level < 212 then return 16/256
elseif self.level < 221 then return 48/256
elseif self.level < 232 then return 80/256
elseif self.level < 244 then return 112/256
elseif self.level < 256 then return 144/256
elseif self.level < 267 then return 176/256
elseif self.level < 277 then return 192/256
elseif self.level < 287 then return 208/256
elseif self.level < 295 then return 224/256
elseif self.level < 300 then return 240/256
else return 20 end
end
function SakuraGame:onLineClear(cleared_row_count)
self.level = self.level + cleared_row_count
for i = 13, 24 do
for j = 1, 10 do
local block = self.grid.grid[i][j]
if block and block.skin == "gem" and block.colour == "X" then
self.time_limit = self.time_limit + 60
end
end
end
end
function SakuraGame:onPieceEnter()
if self.level % 100 ~= 99 and not self.clear and self.stage_frames ~= 0 then
self.level = self.level + 1
end
if effects[self.current_map] == "mirror" and
self.stage_pieces % 3 == 0 and self.stage_pieces ~= 0
then
self.grid:mirror()
end
self.stage_pieces = self.stage_pieces + 1
end
function SakuraGame:advanceOneFrame(inputs, ruleset)
if self.ready_frames == 0 then
if self.lcd > 0 then
if self.stage_frames <= frameTime(0,10) then self.time_extend = 600
elseif self.stage_frames <= frameTime(0,30) then self.time_extend = 300
else self.time_extend = 0 end
end
if not self.grid:hasGemBlocks() or
(self.stage_frames >= 3600 and self.current_map <= 20) then
self.lcd = 0
self.are = 0
if self.stage_frames >= 3600 then self.time_extend = 0 end
self.piece = nil
-- transition to next map
if self.cleared_frames > 0 then
self.cleared_frames = self.cleared_frames - 1
if self.time_extend > 0 then
self.time_limit = self.time_limit + 3
self.time_extend = self.time_extend - 3
end
return false
end
self.hold_queue = nil
if self.current_map > 20 or (self.stage_frames < 3600 and self.current_map <= 20) then self.maps_cleared = self.maps_cleared + 1 end
self.stage_frames = -1
self.level = 0
self.grid:clear()
if (self.current_map == 20) then self.map_20_time = self.frames end
if self.current_map >= 20 and self:checkRequirements() then
self.clear = true
self.completed = true
return false
else
self.current_map = self.current_map + 1
self.big_mode = effects[self.current_map] == "big"
self.ready_frames = 100
self.stage_pieces = 0
self.grid:applyMap(maps[self.current_map])
end
-- this is necessary to fix timer
self.frames = self.frames - 1
self.time_limit = self.time_limit + 1
end
self.frames = self.frames + 1
self.stage_frames = self.stage_frames + 1
self.time_limit = math.max(self.time_limit - 1, 0)
if self.time_limit <= 0 and self.piece == nil then
self.game_over = true
end
if self.piece ~= nil and
effects[self.current_map] == "roll" and
self.stage_pieces % 4 == 0
then
self.piece.colour = "F"
if self.stage_frames % 30 == 0 then
ruleset:attemptRotate(
{[config.gamesettings.world_reverse == 3 or
(ruleset.world and config.gamesettings.world_reverse == 2)
and "rotate_left" or "rotate_right"] = true},
self.piece, self.grid, false
)
end
end
else
self.cleared_frames = STAGE_TRANSITION_TIME
if not self.prev_inputs.hold and inputs.hold then
self.hold_queue = table.remove(self.next_queue, 1)
table.insert(self.next_queue, self:getNextPiece(ruleset))
end
end
return true
end
local function colourXRay(game, block, x, y, age)
local r, g, b, a = .5,.5,.5
if ((game.stage_frames/2 - x) % 30 < 1)
or game.stage_frames == 0
or game.cleared_frames ~= STAGE_TRANSITION_TIME
or game.stage_pieces % 2 == 0
then
a = 1
else
a = 1 - age / 4
end
return r, g, b, a, a
end
local function colourColor(game, block, x, y, age)
local r, g, b, a = .5,.5,.5
if game.stage_frames == 0 or game.cleared_frames ~= STAGE_TRANSITION_TIME then
a = 1
else
a = (game.stage_frames/30 + (y + math.abs(x-5.5))/5) % 1
end
return r, g, b, a, 0
end
function SakuraGame:drawGrid()
if effects[self.current_map] == "xray" then
self.grid:drawCustom(colourXRay, self)
elseif effects[self.current_map] == "color" then
self.grid:drawCustom(colourColor, self)
else
self.grid:draw()
-- if self.piece ~= nil and self.level < 100 then
self:drawGhostPiece(ruleset)
-- end
end
end
function SakuraGame:drawScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
love.graphics.print(
self.das.direction .. " " ..
self.das.frames .. " " ..
strTrueValues(self.prev_inputs)
)
love.graphics.printf("NEXT", 64, 40, 40, "left")
love.graphics.printf("STAGE", 240, 120, 80, "left")
love.graphics.printf("TIME LIMIT", 240, 180, 80, "left")
love.graphics.printf("LEVEL", 240, 320, 40, "left")
if self.current_map <= 20 then
love.graphics.printf("STAGE LIMIT", 240, 240, 100, "left")
end
if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
end
if self.randomizer.history then
love.graphics.printf("RANDOM PIECES ACTIVE!", 240, 150, 200, "left")
end
love.graphics.setFont(font_3x5_3)
love.graphics.setColor(
(self.time_limit % 4 < 2 and
self.time_limit <= frameTime(0,10) and
self.grid:hasGemBlocks() and
self.time_limit ~= 0 and
self.ready_frames == 0) and
{ 1, 0.3, 0.3, 1 } or
{ 1, 1, 1, 1 }
)
love.graphics.printf(formatTime(self.time_limit), 240, 200, 120, "left")
love.graphics.setColor(1, 1, 1, 1)
if self.current_map <= 20 then
love.graphics.printf(formatTime(3600 - self.stage_frames), 240, 260, 120, "left")
end
love.graphics.printf(self.level, 240, 340, 40, "right")
love.graphics.printf(math.floor((self.level + 100) / 100) * 100, 240, 370, 40, "right")
love.graphics.setFont(font_8x11)
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
love.graphics.printf(self.current_map, 290, 110, 80, "left")
end
function SakuraGame:drawCustom()
love.graphics.setColor(1, 1, 1, 1)
if self.ready_frames ~= 0 and not self.clear then
love.graphics.setFont(font_3x5_4)
love.graphics.printf("STAGE " .. self.current_map, 64, 170, 160, "center")
love.graphics.setFont(font_3x5_3)
if effects[self.current_map] then
love.graphics.printf("EFFECT: " .. effects[self.current_map], 64, 270, 160, "center")
end
end
if self.cleared_frames > 0 and
(not self.grid:hasGemBlocks() or
(self.stage_frames >= 3600 and self.current_map <= 20)) then
love.graphics.setFont(font_3x5_2)
love.graphics.printf("TIME LIMIT", 64, 180, 160, "center")
love.graphics.printf("TIME EXTEND", 64, 240, 160, "center")
love.graphics.printf("STAGE TIME", 64, 300, 160, "center")
love.graphics.setFont(font_3x5_3)
love.graphics.printf("STAGE " .. self.current_map, 64, 100, 160, "center")
love.graphics.setColor(
self.cleared_frames % 4 < 2 and
{ 1, 1, 0.3, 1 } or
{ 1, 1, 1, 1 }
)
love.graphics.printf(formatTime(self.time_limit), 64, 200, 160, "center")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(formatTime(self.time_extend), 64, 260, 160, "center")
love.graphics.printf(formatTime(self.stage_frames), 64, 320, 160, "center")
love.graphics.setFont(font_3x5_4)
love.graphics.printf((self.stage_frames >= 3600 and self.current_map <= 20) and "" or "CLEAR!", 64, 130, 160, "center")
end
if self.clear then
love.graphics.setFont(font_3x5_3)
love.graphics.printf("EXCELLENT!", 64, 180, 160, "center")
love.graphics.setFont(font_3x5_2)
if self.current_map ~= 27 then
love.graphics.printf("...but let's go\nbetter next time", 64, 220, 160, "center")
end
end
end
function SakuraGame:getBackground()
return (self.current_map - 1) % 20
end
function SakuraGame:getHighscoreData()
return {
maps = self.maps_cleared,
current_map = self.current_map,
frames = self.frames,
}
end
return SakuraGame

View File

@@ -92,12 +92,8 @@ function SurvivalA3Game:getGarbageLimit()
else return 8 end else return 8 end
end end
function SurvivalA3Game:getNextPiece(ruleset) function SurvivalA3Game:getSkin()
return { return self.level >= 1000 and "bone" or "2tie"
skin = self.level >= 1000 and "bone" or "2tie",
shape = self.randomizer:nextPiece(),
orientation = ruleset:getDefaultOrientation(),
}
end end
function SurvivalA3Game:hitTorikan(old_level, new_level) function SurvivalA3Game:hitTorikan(old_level, new_level)

View File

@@ -0,0 +1,23 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local BagRandomizer = Randomizer:extend()
function BagRandomizer:new(pieces)
self.bag = {}
self.pieces = pieces
for i = 1, self.pieces do
table.insert(self.bag, i)
end
end
function BagRandomizer:generatePiece()
if next(self.bag) == nil then
for i = 1, self.pieces do
table.insert(self.bag, i)
end
end
local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x)
end
return BagRandomizer

View File

@@ -0,0 +1,16 @@
local Randomizer = require 'tetris.randomizers.randomizer'
local Sequence = Randomizer:extend()
function Sequence:initialize()
self.sequence = "IJLOT"
self.counter = 0
end
function Sequence:generatePiece()
local piece = string.sub(self.sequence, self.counter + 1, self.counter + 1)
self.counter = (self.counter + 1) % string.len(self.sequence)
return piece
end
return Sequence

View File

@@ -3,6 +3,7 @@ local Object = require 'libs.classic'
local Randomizer = Object:extend() local Randomizer = Object:extend()
function Randomizer:new() function Randomizer:new()
self.possible_pieces = 7
self:initialize() self:initialize()
end end

View File

@@ -0,0 +1,10 @@
local Sequence = require 'tetris.randomizers.fixed_sequence'
local Sakura = Sequence:extend()
function Sakura:initialize()
self.super:initialize()
self.sequence = "LIJOTSZILJOTISJZLOIJSZTIOJZTLSOZTISOLTJSIZTOJLIZSTOIZLTJOSILTZSOITJLZSTJJISOLJITSLZOIZSJOITSZLJTSZLISTJLZOTIOZSJILTZSOITZJSOLTJSZIOJLZIOJTZIZLOSIZTJOILZSOJIOSZTJILOSSILZOTJIZTSOLZTSOIJTZSILTZOSIJZTOLJISOLJTZSOLTZJSOTILZJTOLZIJSOZTJLOZSTLOZITSOLZTJIOSLZJTO"
end
return Sakura

View File

@@ -49,6 +49,7 @@ function ARS:onPieceMove(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end
@@ -59,6 +60,7 @@ function ARS:onPieceRotate(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end

View File

@@ -36,6 +36,7 @@ function ARS:onPieceMove(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end
@@ -46,6 +47,7 @@ function ARS:onPieceRotate(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then if piece.manipulations >= 128 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end

View File

@@ -6,6 +6,8 @@ local SRS = Ruleset:extend()
SRS.name = "ACE-SRS" SRS.name = "ACE-SRS"
SRS.hash = "ACE Standard" SRS.hash = "ACE Standard"
SRS.MANIPULATIONS_MAX = 128
SRS.spawn_positions = { SRS.spawn_positions = {
I = { x=5, y=2 }, I = { x=5, y=2 },
J = { x=4, y=3 }, J = { x=4, y=3 },
@@ -26,21 +28,11 @@ SRS.big_spawn_positions = {
Z = { x=2, y=1 }, Z = { x=2, y=1 },
} }
function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then
piece.locked = true
end
end
end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 128 then if piece.manipulations >= self.MANIPULATIONS_MAX then
piece.locked = true piece.locked = true
end end
end end

View File

@@ -406,6 +406,7 @@ function CRS:onPieceMove(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.move_counter = piece.move_counter + 1 piece.move_counter = piece.move_counter + 1
if piece.move_counter >= 24 then if piece.move_counter >= 24 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end
@@ -415,6 +416,7 @@ function CRS:onPieceRotate(piece, grid)
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.rotate_counter = piece.rotate_counter + 1 piece.rotate_counter = piece.rotate_counter + 1
if piece.rotate_counter >= 12 then if piece.rotate_counter >= 12 then
piece:dropToBottom(grid)
piece.locked = true piece.locked = true
end end
end end

254
tetris/rulesets/pairs.lua Normal file
View File

@@ -0,0 +1,254 @@
local Ruleset = require 'tetris.rulesets.ruleset'
local PAIRS = Ruleset:extend()
PAIRS.name = "PAIRS"
PAIRS.hash = "PAIRS"
PAIRS.world = true
PAIRS.spawn_positions = {
[1] = { x=4, y=4 },
[2] = { x=4, y=5 },
[3] = { x=4, y=5 },
[4] = { x=4, y=5 },
[5] = { x=5, y=5 },
[6] = { x=5, y=5 },
[7] = { x=5, y=5 },
[8] = { x=5, y=5 },
[9] = { x=5, y=5 },
[10] = { x=5, y=5 },
[11] = { x=4, y=5 },
[12] = { x=4, y=5 },
[13] = { x=4, y=5 },
[14] = { x=4, y=5 },
[15] = { x=4, y=5 },
[16] = { x=4, y=5 },
[17] = { x=4, y=5 },
[18] = { x=4, y=5 },
}
PAIRS.big_spawn_positions = {
[1] = { x=2, y=2 },
[2] = { x=2, y=3 },
[3] = { x=2, y=3 },
[4] = { x=2, y=3 },
[5] = { x=3, y=3 },
[6] = { x=3, y=3 },
[7] = { x=3, y=3 },
[8] = { x=3, y=3 },
[9] = { x=3, y=3 },
[10] = { x=3, y=3 },
[11] = { x=2, y=3 },
[12] = { x=2, y=3 },
[13] = { x=2, y=3 },
[14] = { x=2, y=3 },
[15] = { x=2, y=3 },
[16] = { x=2, y=3 },
[17] = { x=2, y=3 },
[18] = { x=2, y=3 },
}
PAIRS.next_sounds = {
[1] = "I",
[2] = "O",
[3] = "S",
[4] = "Z",
[5] = "L",
[6] = "J",
[7] = "Z",
[8] = "S",
[9] = "J",
[10] = "L",
[11] = "O",
[12] = "O",
[13] = "T",
[14] = "L",
[15] = "J",
[16] = "T",
[17] = "J",
[18] = "I"
}
PAIRS.colourscheme = {
[1] = "R",
[2] = "C",
[3] = "G",
[4] = "M",
[5] = "O",
[6] = "C",
[7] = "G",
[8] = "M",
[9] = "G",
[10] = "M",
[11] = "Y",
[12] = "B",
[13] = "M",
[14] = "O",
[15] = "B",
[16] = "G",
[17] = "C",
[18] = "R"
}
PAIRS.pieces = 18
PAIRS.block_offsets = {
[1]={
{ {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} },
{ {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} },
{ {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} },
{ {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} },
},
[2]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-1} },
},
[3]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0}, {x=-1, y=-2} },
},
[4]={
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0}, {x=1, y=-2} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=0}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0}, {x=1, y=-2} },
},
[5]={
{ {x=1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=0}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-2, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-3}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[6]={
{ {x=-2, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-3}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=1, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=0}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[7]={
{ {x=-2, y=-1}, {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-3}, {x=0, y=-2}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
{ {x=-1, y=0}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=-1, y=-1} },
},
[8]={
{ {x=1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
{ {x=0, y=0}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1} },
{ {x=-2, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-3}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[9]={
{ {x=-1, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-2}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=0, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-1}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[10]={
{ {x=0, y=-1}, {x=-2, y=0}, {x=-1, y=0}, {x=0, y=0}, {x=1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-3}, {x=-1, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
{ {x=-1, y=0}, {x=-2, y=-1}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1} },
{ {x=-1, y=-2}, {x=0, y=-3}, {x=0, y=-2}, {x=0, y=-1}, {x=0, y=0} },
},
[11]={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-1}, {x=1, y=-2} },
{ {x=0, y=0}, {x=1, y=-1}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-1}, {x=-1, y=0} },
},
[12]={
{ {x=0, y=0}, {x=1, y=-1}, {x=1, y=0}, {x=0, y=-1}, {x=-1, y=0} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-1}, {x=1, y=0} },
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=-1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-1}, {x=-1, y=-2} },
},
[13]={
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=0}, {x=1, y=-2} },
{ {x=0, y=-1}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=0}, {x=-1, y=-2} },
},
[14]={
{ {x=0, y=-1}, {x=0, y=0}, {x=0, y=-2}, {x=-1, y=-1}, {x=1, y=0} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=-2}, {x=-1, y=0} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-1}, {x=1, y=-1}, {x=-1, y=-1}, {x=0, y=0}, {x=1, y=-2} },
},
[15]={
{ {x=0, y=-1}, {x=0, y=0}, {x=0, y=-2}, {x=-1, y=0}, {x=1, y=-1} },
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=-2}, {x=0, y=0} },
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=1, y=-2}, {x=-1, y=-1} },
{ {x=0, y=-1}, {x=1, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=-2} },
},
[16]={
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=0}, {x=1, y=0} },
{ {x=-1, y=0}, {x=0, y=-1}, {x=-1, y=-2}, {x=-1, y=-1}, {x=1, y=-1} },
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2}, {x=-1, y=-2}, {x=1, y=-2} },
{ {x=1, y=0}, {x=0, y=-1}, {x=1, y=-2}, {x=-1, y=-1}, {x=1, y=-1} },
},
[17]={
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-2}, {x=1, y=-2}, {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=0, y=-2}, {x=1, y=0}, {x=-1, y=-2}, {x=1, y=-1}, {x=1, y=-2} },
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=-1}, {x=1, y=-2} },
},
[18]={
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1}, {x=-1, y=-2} },
{ {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2}, {x=1, y=-2} },
{ {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=-1}, {x=1, y=-1}, {x=1, y=0} },
{ {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
},
}
PAIRS.wallkicks = {
{x=1, y=0}, {x=-1, y=0}, {x=2, y=0}, {x=-2, y=0}, {x=0, y=-1}
}
function PAIRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
if (piece.shape == 2) then return end
local kicks = PAIRS.wallkicks
assert(piece.rotation ~= new_piece.rotation)
for idx, offset in pairs(kicks) do
kicked_piece = new_piece:withOffset(offset)
if grid:canPlacePiece(kicked_piece) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir)
piece:setOffset(offset)
return
end
end
end
function PAIRS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y
if y > piece.lowest_y then
piece.lock_delay = 0
piece.lowest_y = y
end
end
end
function PAIRS:onPieceCreate(piece, grid)
piece.lowest_y = -math.huge
end
function PAIRS:onPieceDrop(piece, grid)
self:checkNewLow(piece)
end
function PAIRS:get180RotationValue()
if config.gamesettings.world_reverse == 1 then
return 1
else
return 3
end
end
return PAIRS

View File

@@ -22,10 +22,24 @@ Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false Ruleset.enable_IRS_wallkicks = false
Ruleset.are_cancel = false Ruleset.are_cancel = false
Ruleset.are = true
Ruleset.next_sounds = {
I = "I",
L = "L",
J = "J",
S = "S",
Z = "Z",
O = "O",
T = "T"
}
Ruleset.pieces = 7
-- Component functions. -- Component functions.
function Ruleset:new() function Ruleset:new()
if config.gamesettings.piece_colour == 1 then if config.gamesettings.piece_colour == 1 then
blocks["bone"] = (not self.world) and blocks["bone"] = (not self.world) and
{ {
@@ -115,8 +129,8 @@ function Ruleset:attemptRotate(new_inputs, piece, grid, initial)
local new_piece = piece:withRelativeRotation(rot_dir) local new_piece = piece:withRelativeRotation(rot_dir)
if (grid:canPlacePiece(new_piece)) then if (grid:canPlacePiece(new_piece)) then
self:onPieceRotate(piece, grid)
piece:setRelativeRotation(rot_dir) piece:setRelativeRotation(rot_dir)
self:onPieceRotate(piece, grid)
else else
if not(initial and self.enable_IRS_wallkicks == false) then if not(initial and self.enable_IRS_wallkicks == false) then
self:attemptWallkicks(piece, new_piece, rot_dir, grid) self:attemptWallkicks(piece, new_piece, rot_dir, grid)
@@ -181,7 +195,9 @@ function Ruleset:getDefaultOrientation() return 1 end
function Ruleset:initializePiece( function Ruleset:initializePiece(
inputs, data, grid, gravity, prev_inputs, inputs, data, grid, gravity, prev_inputs,
move, lock_delay, drop_speed, move, lock_delay, drop_speed,
drop_locked, hard_drop_locked, big, irs drop_locked, hard_drop_locked, big, irs,
buffer_hard_drop, buffer_soft_drop,
lock_on_hard_drop, lock_on_soft_drop
) )
local spawn_positions local spawn_positions
if big then if big then
@@ -206,6 +222,13 @@ function Ruleset:initializePiece(
self:rotatePiece(inputs, piece, grid, {}, true) self:rotatePiece(inputs, piece, grid, {}, true)
end end
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked) self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
if (buffer_hard_drop and config.gamesettings.buffer_lock == 1) then
piece:dropToBottom(grid)
if lock_on_hard_drop then piece.locked = true end
end
if (buffer_soft_drop and lock_on_soft_drop and piece:isDropBlocked(grid) and config.gamesettings.buffer_lock == 1) then
piece.locked = true
end
return piece return piece
end end

View File

@@ -8,7 +8,9 @@ SRS.hash = "Standard"
SRS.enable_IRS_wallkicks = true SRS.enable_IRS_wallkicks = true
function SRS:check_new_low(piece) SRS.MANIPULATIONS_MAX = 15
function SRS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y local y = piece.position.y + block.y
if y > piece.lowest_y then if y > piece.lowest_y then
@@ -74,8 +76,8 @@ function SRS:onPieceCreate(piece, grid)
end end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
self:check_new_low(piece) self:checkNewLow(piece)
if piece.manipulations >= 15 and piece:isDropBlocked(grid) then if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true piece.locked = true
else else
piece.lock_delay = 0 -- step reset piece.lock_delay = 0 -- step reset
@@ -86,7 +88,7 @@ function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 15 then if piece.manipulations >= self.MANIPULATIONS_MAX then
piece.locked = true piece.locked = true
end end
end end
@@ -94,13 +96,14 @@ end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
self:check_new_low(piece) self:checkNewLow(piece)
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece:isDropBlocked(grid) then if piece.manipulations >= self.MANIPULATIONS_MAX then
if piece.manipulations >= 15 then piece:moveInGrid({ x = 0, y = 1 }, 1, grid)
piece.locked = true if piece:isDropBlocked(grid) then
end piece.locked = true
end end
end
end end
function SRS:get180RotationValue() return 2 end function SRS:get180RotationValue() return 2 end

View File

@@ -18,6 +18,9 @@ SRS.colourscheme = {
SRS.softdrop_lock = false SRS.softdrop_lock = false
SRS.harddrop_lock = true SRS.harddrop_lock = true
SRS.MANIPULATIONS_MAX = 10
SRS.ROTATIONS_MAX = 8
SRS.spawn_positions = { SRS.spawn_positions = {
I = { x=5, y=4 }, I = { x=5, y=4 },
J = { x=4, y=5 }, J = { x=4, y=5 },
@@ -162,14 +165,18 @@ function SRS:onPieceCreate(piece, grid)
end end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
piece.lock_delay = 0 -- step reset if (piece.manipulations >= self.MANIPULATIONS_MAX or piece.rotations >= self.ROTATIONS_MAX) and piece:isDropBlocked(grid) then
piece.locked = true
else
piece.lock_delay = 0 -- step reset
end
end end
function SRS:onPieceMove(piece, grid) function SRS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1 piece.manipulations = piece.manipulations + 1
if piece.manipulations >= 10 then if piece.manipulations >= self.MANIPULATIONS_MAX then
piece.locked = true piece.locked = true
end end
end end
@@ -178,8 +185,8 @@ end
function SRS:onPieceRotate(piece, grid) function SRS:onPieceRotate(piece, grid)
piece.lock_delay = 0 -- rotate reset piece.lock_delay = 0 -- rotate reset
if piece:isDropBlocked(grid) then if piece:isDropBlocked(grid) then
piece.rotations = piece.rotations + 1 piece.rotations = piece.rotations + 1
if piece.rotations >= 8 then if piece.rotations >= self.ROTATIONS_MAX then
piece.locked = true piece.locked = true
end end
end end