Compare commits

..

11 Commits

Author SHA1 Message Date
Hailey
e42e0225cf cleanup before pr 2021-11-14 17:49:27 +10:00
Hailey
d937ce8507 added information on haileyjunk in case people wonder what it is 2021-11-14 17:41:40 +10:00
Hailey
f994d768ef Added freeplay mode details and challenge details!! 2021-11-14 15:07:07 +10:00
Hailey
9f5f9bc336 Implemented challenges!!!!! 2021-11-14 14:18:55 +10:00
Hailey
23b42a534e Merge branch 'master' into haileyjunk 2021-11-09 14:58:14 +10:00
hailey
6a465f8441 changed version for screenshots 2021-10-22 20:37:49 +10:00
hailey
8c5564f196 debug keys and splash text i guess 2021-10-22 15:39:39 +10:00
hailey
035a3aa2f2 Merge branch 'master' into haileyjunk 2021-10-22 15:09:03 +10:00
hailey
9f88faa0a7 wip bs 2021-10-22 15:08:14 +10:00
hailey
63e9531b28 h 2021-09-30 12:46:38 +10:00
hailey
174c45e7eb new branch where i practice getting back into the coding groove while testing and experimenting with new and interesing ideas and concepts 2021-09-30 11:38:20 +10:00
48 changed files with 564 additions and 683 deletions

View File

@@ -57,13 +57,13 @@ Coding conventions
Use tabs to indent, spaces to align. Use tabs to indent, spaces to align.
* Specifically, spaces should not appear at the beginning of a line, and tabs should not appear _except_ at the beginning of a line. * Specifically, spaces should not appear at the beginning of a line, and tabs should not appear _except_ at the beginning of a line.
* If you're aligning multiline if-statements, the initial "if", "elseif" or "else" should be flush left with the indentation level, with spaces padding the gap to the next word as necessary. For example: * The sole exception is in a multiline `if` statement; the initial `if` should have four spaces before it to align it with an `elseif` on the next line. For example:
```lua ```lua
if self.level < 900 then return 12 ---- 4 spaces
elseif self.level < 1200 then return 8 if self.level < 900 then return 12
else return 6 elseif self.level < 1200 then return 8
end else return 6 end
``` ```
Comments at the end of lines of code must be one line long. Multi-line comments must appear in their own block. Comments at the end of lines of code must be one line long. Multi-line comments must appear in their own block.

View File

@@ -26,13 +26,13 @@ All assets needed are bundled with the executable.
#### Bleeding edge #### Bleeding edge
If you want the bleeding edge version, download [this](https://github.com/MillaBasset/cambridge/archive/master.zip). Extract the ZIP to a folder of your choosing. If you want the bleeding edge version, download [this](https://github.com/MillaBasset/cambridge/archive/master.zip).
Assuming you're on a 64-bit system, you can double-click `start_win64.bat` to run the game. If that doesn't work, open a Command Prompt where you extracted Cambridge and run: Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
dist\windows\love.exe . dist\windows\love.exe .
If you're on a 32-bit system, you'll want to double-click `start_win32.bat`. If that doesn't work, run this instead: Alternatively, if you're on a 32-bit system, run this instead:
dist\win32\love.exe . dist\win32\love.exe .
@@ -40,9 +40,17 @@ If you're on a 32-bit system, you'll want to double-click `start_win32.bat`. If
Then, check the mod pack section at the bottom of this page. Then, check the mod pack section at the bottom of this page.
#### Other branches/haileyjunk
The Cambridge repository has multiple public and private branches for working on and testing out major new features over the course of many commits.
If you're looking to test even newer features that aren't even in the master branch, the closest you're going to get is probably the haileyjunk branch, which you can download [here](https://github.com/MillaBasset/cambridge/archive/master.zip).
> WARNING: This branch can be very buggy and broken at times. Use with care.
### macOS, Linux ### macOS, Linux
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.3, because it won't work with earlier or later 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 #### Downloading a release
@@ -102,6 +110,7 @@ Other Notable Games
- [Tetra Online](https://github.com/Juan-Cartes/Tetra-Offline) by Mine - [Tetra Online](https://github.com/Juan-Cartes/Tetra-Offline) by Mine
- [Techmino](https://discord.gg/6Yuww44tq8) by MrZ - [Techmino](https://discord.gg/6Yuww44tq8) by MrZ
- [Example Block Game](https://github.com/oshisaure/example-block-game) by Oshisaure - [Example Block Game](https://github.com/oshisaure/example-block-game) by Oshisaure
- [TETR.IO](https://tetr.io) by osk
- [Master of Blocks](https://discord.gg/72FZ49mjWh) by Phoenix Flare - [Master of Blocks](https://discord.gg/72FZ49mjWh) by Phoenix Flare
- [Spirit Drop](https://rayblastgames.com/spiritdrop.php) by RayRay26 - [Spirit Drop](https://rayblastgames.com/spiritdrop.php) by RayRay26
- [Puzzle Trial](https://kagamine-rin.itch.io/puzzle-trial) by Rin - [Puzzle Trial](https://kagamine-rin.itch.io/puzzle-trial) by Rin

View File

@@ -5,6 +5,8 @@ bgm = {
pacer_test = love.audio.newSource("res/bgm/pacer_test.mp3", "stream"), pacer_test = love.audio.newSource("res/bgm/pacer_test.mp3", "stream"),
} }
local frames = 0
local current_bgm = nil local current_bgm = nil
local bgm_locked = false local bgm_locked = false
local unfocused = false local unfocused = false

View File

@@ -20,7 +20,6 @@ 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"),
title_no_icon = love.graphics.newImage("res/backgrounds/title-no-icon.jpg"),
title_night = love.graphics.newImage("res/backgrounds/title-night.jpg"), title_night = love.graphics.newImage("res/backgrounds/title-night.jpg"),
snow = love.graphics.newImage("res/backgrounds/snow.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"),
@@ -119,7 +118,8 @@ misc_graphics = {
ready = love.graphics.newImage("res/img/ready.png"), ready = love.graphics.newImage("res/img/ready.png"),
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"),
select_challenge = love.graphics.newImage("res/img/select_challenge_placeholder.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"), santa = love.graphics.newImage("res/img/santa.png"),
icon = love.graphics.newImage("res/img/cambridge_transparent.png") icon = love.graphics.newImage("res/img/cambridge_transparent.png")
} }

View File

@@ -1 +1 @@
version = "v0.3.2" version = "v0.3.1"

View File

@@ -13,14 +13,14 @@ function love.load()
loadSave() loadSave()
require "funcs" require "funcs"
require "scene" require "scene"
--config["side_next"] = false --config["side_next"] = false
--config["reverse_rotate"] = true --config["reverse_rotate"] = true
--config["das_last_key"] = false --config["das_last_key"] = false
--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});
-- used for screenshots -- used for screenshots
GLOBAL_CANVAS = love.graphics.newCanvas() GLOBAL_CANVAS = love.graphics.newCanvas()
@@ -35,8 +35,6 @@ function love.load()
end end
function initModules() function initModules()
-- replays are not loaded here, but they are cleared
replays = {}
game_modes = {} game_modes = {}
mode_list = love.filesystem.getDirectoryItems("tetris/modes") mode_list = love.filesystem.getDirectoryItems("tetris/modes")
for i=1,#mode_list do for i=1,#mode_list do
@@ -44,6 +42,13 @@ function initModules()
game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5)) game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5))
end end
end end
challenges = {}
challenge_list = love.filesystem.getDirectoryItems("tetris/challenges")
for i=1,#challenge_list do
if(challenge_list[i] ~= "challenge.lua" and string.sub(challenge_list[i], -4) == ".lua") then
challenges[#challenges+1] = require ("tetris.challenges."..string.sub(challenge_list[i],1,-5))
end
end
rulesets = {} rulesets = {}
rule_list = love.filesystem.getDirectoryItems("tetris/rulesets") rule_list = love.filesystem.getDirectoryItems("tetris/rulesets")
for i=1,#rule_list do for i=1,#rule_list do
@@ -74,20 +79,20 @@ function love.draw()
(height - scale_factor * 480) / 2 (height - scale_factor * 480) / 2
) )
love.graphics.scale(scale_factor) love.graphics.scale(scale_factor)
scene:render() scene:render()
if config.gamesettings.display_gamemode == 1 or scene.title == "Title" then if config.gamesettings.display_gamemode == 1 or scene.title == "Title" then
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf( love.graphics.printf(
string.format("%.2f", 1.0 / love.timer.getAverageDelta()) .. string.format("%.2f", 1 / love.timer.getAverageDelta()) ..
"fps - " .. version, 0, 460, 635, "right" "fps - " .. version, 0, 460, 635, "right"
) )
end end
love.graphics.pop() love.graphics.pop()
love.graphics.setCanvas() love.graphics.setCanvas()
love.graphics.setColor(1,1,1,1) love.graphics.setColor(1,1,1,1)
love.graphics.draw(GLOBAL_CANVAS) love.graphics.draw(GLOBAL_CANVAS)
@@ -118,11 +123,11 @@ function love.keypressed(key, scancode)
love.filesystem.remove("ss") love.filesystem.remove("ss")
love.filesystem.createDirectory("ss") love.filesystem.createDirectory("ss")
end end
print("Saving screenshot as "..love.filesystem.getSaveDirectory().."/"..ss_name) print("Saving screenshot as "..ss_name)
GLOBAL_CANVAS:newImageData():encode("png", ss_name) GLOBAL_CANVAS:newImageData():encode("png", ss_name)
-- function keys are reserved -- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f[1-9][0-9]+$") then
return return
-- escape is reserved for menu_back -- escape is reserved for menu_back
elseif scancode == "escape" then elseif scancode == "escape" then
scene:onInputPress({input="menu_back", type="key", key=key, scancode=scancode}) scene:onInputPress({input="menu_back", type="key", key=key, scancode=scancode})
@@ -142,7 +147,7 @@ function love.keyreleased(key, scancode)
scene:onInputRelease({input="menu_back", type="key", key=key, scancode=scancode}) scene:onInputRelease({input="menu_back", type="key", key=key, scancode=scancode})
-- 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
-- handle all other keys; tab is reserved, but the input config scene keeps it from getting configured as a game input, so pass tab to the scene here -- handle all other keys; tab is reserved, but the input config scene keeps it from getting configured as a game input, so pass tab to the scene here
else else
local input_released = nil local input_released = nil
@@ -188,7 +193,7 @@ function love.joystickaxis(joystick, axis, value)
config.input.joysticks and config.input.joysticks and
config.input.joysticks[joystick:getName()] and config.input.joysticks[joystick:getName()] and
config.input.joysticks[joystick:getName()].axes and config.input.joysticks[joystick:getName()].axes and
config.input.joysticks[joystick:getName()].axes[axis] config.input.joysticks[joystick:getName()].axes[axis]
then then
if math.abs(value) >= 1 then if math.abs(value) >= 1 then
input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"] input_pressed = config.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
@@ -290,7 +295,6 @@ function love.resize(w, h)
end end
local TARGET_FPS = 60 local TARGET_FPS = 60
local FRAME_DURATION = 1.0 / TARGET_FPS
function love.run() function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
@@ -300,7 +304,7 @@ function love.run()
local dt = 0 local dt = 0
local last_time = love.timer.getTime() local last_time = love.timer.getTime()
local time_accumulator = 0.0 local time_accumulator = 0
return function() return function()
if love.event then if love.event then
love.event.pump() love.event.pump()
@@ -317,43 +321,28 @@ function love.run()
if love.timer then if love.timer then
processBGMFadeout(love.timer.step()) processBGMFadeout(love.timer.step())
end end
if scene and scene.update and love.timer then if scene and scene.update and love.timer then
scene:update() scene:update()
if time_accumulator < FRAME_DURATION then local frame_duration = 1.0 / TARGET_FPS
if time_accumulator < frame_duration then
if love.graphics and love.graphics.isActive() and love.draw then if love.graphics and love.graphics.isActive() and love.draw then
love.graphics.origin() love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.clear(love.graphics.getBackgroundColor())
love.draw() love.draw()
love.graphics.present() love.graphics.present()
end end
local end_time = last_time + frame_duration
-- request 1ms delays first but stop short of overshooting, then do "0ms" delays without overshooting (0ms requests generally do a delay of some nonzero amount of time, but maybe less than 1ms) local time = love.timer.getTime()
for milliseconds=0.001,0.000,-0.001 do while time < end_time do
local max_delay = 0.0 love.timer.sleep(0.001)
while max_delay < FRAME_DURATION do time = love.timer.getTime()
local delay_start_time = love.timer.getTime()
if delay_start_time - last_time < FRAME_DURATION - max_delay then
love.timer.sleep(milliseconds)
local last_delay = love.timer.getTime() - delay_start_time
if last_delay > max_delay then
max_delay = last_delay
end
else
break
end
end
end
while love.timer.getTime() - last_time < FRAME_DURATION do
-- busy loop, do nothing here until delay is finished; delays above stop short of finishing, so this part can finish it off precisely
end end
time_accumulator = time_accumulator + time - last_time
end end
time_accumulator = time_accumulator - frame_duration
local finish_delay_time = love.timer.getTime()
local real_frame_duration = finish_delay_time - last_time
time_accumulator = time_accumulator + real_frame_duration - FRAME_DURATION
last_time = finish_delay_time
end end
last_time = love.timer.getTime()
end end
end end

View File

@@ -1,3 +1 @@
#!/bin/sh
zip -r cambridge.love libs load res scene tetris conf.lua main.lua scene.lua funcs.lua zip -r cambridge.love libs load res scene tetris conf.lua main.lua scene.lua funcs.lua

View File

@@ -1,6 +1,4 @@
#!/bin/sh ./package
./package-love.sh
mkdir dist mkdir dist
mkdir dist/windows mkdir dist/windows
mkdir dist/win32 mkdir dist/win32
@@ -10,4 +8,4 @@ zip dist/cambridge-windows.zip dist/windows/* SOURCES.md LICENSE.md
cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe
zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE.md zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE.md
cp cambridge.love dist/other/ cp cambridge.love dist/other/
zip dist/cambridge-other.zip cambridge.love libs/discord-rpc.* SOURCES.md LICENSE.md zip dist/cambridge-other.zip cambridge.love libs/discord-rpc.* SOURCES.md LICENSE.md

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -10,9 +10,9 @@ function Scene:onInputRelease() end
ExitScene = require "scene.exit" ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"
ReplayScene = require "scene.replay" ChallengeScene = require "scene.challenge"
ModeSelectScene = require "scene.mode_select" ModeSelectScene = require "scene.mode_select"
ReplaySelectScene = require "scene.replay_select" ChallengeSelectScene = require "scene.challenge_select"
KeyConfigScene = require "scene.key_config" KeyConfigScene = require "scene.key_config"
StickConfigScene = require "scene.stick_config" StickConfigScene = require "scene.stick_config"
InputConfigScene = require "scene.input_config" InputConfigScene = require "scene.input_config"

93
scene/challenge.lua Normal file
View File

@@ -0,0 +1,93 @@
local ChallengeScene = Scene:extend()
ChallengeScene.title = "Challenge"
require 'load.save'
function ChallengeScene:new(game_mode, ruleset, inputs)
self.retry_mode = game_mode
self.retry_ruleset = ruleset
self.secret_inputs = inputs
self.game = game_mode(self.secret_inputs)
self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset)
self.inputs = {
left=false,
right=false,
up=false,
down=false,
rotate_left=false,
rotate_left2=false,
rotate_right=false,
rotate_right2=false,
rotate_180=false,
hold=false,
}
self.paused = false
DiscordRPC:update({
details = "In challenge",
state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
function ChallengeScene:update()
if love.window.hasFocus() and not self.paused then
local inputs = {}
for input, value in pairs(self.inputs) do
inputs[input] = value
end
self.game:update(inputs, self.ruleset)
self.game.grid:update()
DiscordRPC:update({
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
end
function ChallengeScene:render()
self.game:draw(self.paused)
end
function ChallengeScene:onInputPress(e)
if (
self.game.game_over or self.game.completed
) and (
e.input == "menu_decide" or
e.input == "menu_back" or
e.input == "retry"
) then
highscore_entry = self.game:getHighscoreData()
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
submitHighscore(highscore_hash, highscore_entry)
self.game:onExit()
scene = e.input == "retry" and ChallengeScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
elseif e.input == "retry" then
switchBGM(nil)
self.game:onExit()
scene = ChallengeScene(self.retry_mode, self.retry_ruleset, self.secret_inputs)
elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
self.paused = not self.paused
if self.paused then pauseBGM()
else resumeBGM() end
elseif e.input == "menu_back" then
self.game:onExit()
scene = ChallengeSelectScene()
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = true
end
end
function ChallengeScene:onInputRelease(e)
if e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = false
end
end
function submitHighscore(hash, data)
if not highscores[hash] then highscores[hash] = {} end
table.insert(highscores[hash], data)
saveHighscores()
end
return ChallengeScene

186
scene/challenge_select.lua Normal file
View File

@@ -0,0 +1,186 @@
local ChallengeSelectScene = Scene:extend()
ChallengeSelectScene.title = "Challenges"
current_challenge = 1
function indexOf(array, value)
for i, v in ipairs(array) do
if v == value then
return i
end
end
return nil
end
function ChallengeSelectScene:new()
-- reload custom modules
initModules()
if table.getn(challenges) == 0 then
self.display_warning = true
current_challenge = 1
else
self.display_warning = false
if current_challenge > table.getn(challenges) then
current_challenge = 1
end
end
self.menu_state = {
challenge = current_challenge,
select = "challenge",
}
self.secret_inputs = {}
self.das = 0
DiscordRPC:update({
details = "In menus",
state = "Choosing a challenge",
largeImageKey = "ingame-000"
})
end
function ChallengeSelectScene:update()
switchBGM(nil) -- experimental
if self.das_up or self.das_down then
self.das = self.das + 1
else
self.das = 0
end
if self.das >= 15 then
self:changeOption(self.das_up and -1 or 1)
self.das = self.das - 4
end
DiscordRPC:update({
details = "In menus",
state = "Choosing a challenge",
largeImageKey = "ingame-000"
})
end
function ChallengeSelectScene:render()
love.graphics.draw(
backgrounds[0],
0, 0, 0,
0.5, 0.5
)
love.graphics.draw(misc_graphics["select_challenge"], 20, 40)
if self.display_warning then
love.graphics.setFont(font_3x5_3)
love.graphics.printf(
"You have no challenges",
80, 200, 480, "center"
)
love.graphics.setFont(font_3x5_2)
love.graphics.printf(
"Come back to this menu after getting more challenges. " ..
"Press any button to return to the main menu.",
80, 250, 480, "center"
)
return
end
if self.menu_state.select == "challenge" then
love.graphics.setColor(1, 1, 1, 0.5)
end
love.graphics.rectangle("fill", 20, 258, 240, 22)
if self.menu_state.select == "challenge" then
love.graphics.setColor(1, 1, 1, 0.25)
end
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
for idx, challenge in pairs(challenges) do
if(idx >= self.menu_state.challenge-9 and idx <= self.menu_state.challenge+9) then
love.graphics.printf(challenge.name, 40, (260 - 20*(self.menu_state.challenge)) + 20 * idx, 200, "left")
end
end
-- challenge details
love.graphics.printf(challenges[self.menu_state.challenge].tagline, 340, 150, 200, "left")
love.graphics.printf(challenges[self.menu_state.challenge].description, 340, 250, 200, "left")
love.graphics.printf("Mode: "..challenges[self.menu_state.challenge].mode, 340, 350, 200, "left")
love.graphics.printf("Ruleset: "..challenges[self.menu_state.challenge].ruleset, 340, 380, 200, "left")
end
function ChallengeSelectScene:onInputPress(e)
if self.display_warning and e.input then
scene = TitleScene()
elseif e.type == "wheel" then
if e.x % 2 == 1 then
self:switchSelect()
end
if e.y ~= 0 then
self:changeOption(-e.y)
end
elseif e.input == "menu_decide" or e.scancode == "return" then
for idx, mode in pairs(game_modes) do
if mode.hash == challenges[self.menu_state.challenge].mode then
cur_mode = idx
break
end
end
for idx, ruleset in pairs(rulesets) do
if ruleset.hash == challenges[self.menu_state.challenge].ruleset then
cur_ruleset = idx
break
end
end
playSE("mode_decide")
saveConfig()
scene = ChallengeScene(
challenges[current_challenge],
rulesets[cur_ruleset]
)
elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1)
self.das_up = true
self.das_down = nil
elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1)
self.das_down = true
self.das_up = nil
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene()
elseif e.input then
self.secret_inputs[e.input] = true
end
end
function ChallengeSelectScene:onInputRelease(e)
if e.input == "up" or e.scancode == "up" then
self.das_up = nil
elseif e.input == "down" or e.scancode == "down" then
self.das_down = nil
elseif e.input then
self.secret_inputs[e.input] = false
end
end
function ChallengeSelectScene:changeOption(rel)
self:changechallenge(rel)
playSE("cursor")
end
function ChallengeSelectScene:changechallenge(rel)
local len = table.getn(challenges)
self.menu_state.challenge = Mod1(self.menu_state.challenge + rel, len)
end
return ChallengeSelectScene

View File

@@ -5,7 +5,7 @@ CreditsScene.title = "Credits"
function CreditsScene:new() function CreditsScene:new()
self.frames = 0 self.frames = 0
-- higher = slower -- higher = slower
self.scroll_speed = 1.8 self.scroll_speed = 1.85
switchBGM("credit_roll", "gm3") switchBGM("credit_roll", "gm3")
DiscordRPC:update({ DiscordRPC:update({
@@ -46,7 +46,7 @@ function CreditsScene:render()
love.graphics.print("Game Developers", 320, 550 - offset) love.graphics.print("Game Developers", 320, 550 - offset)
love.graphics.print("Project Heads", 320, 640 - offset) love.graphics.print("Project Heads", 320, 640 - offset)
love.graphics.print("Notable Game Developers", 320, 750 - offset) love.graphics.print("Notable Game Developers", 320, 750 - offset)
love.graphics.print("Special Thanks", 320, 1000 - offset) love.graphics.print("Special Thanks", 320, 1020 - offset)
love.graphics.print("- Milla", 320, math.max(2130 - offset, 320)) love.graphics.print("- Milla", 320, math.max(2130 - offset, 320))
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
@@ -55,7 +55,7 @@ function CreditsScene:render()
love.graphics.print( love.graphics.print(
"2Tie - TGMsim\nAxel Fox - Multimino\nDr Ocelot - Tetra Legends\n" .. "2Tie - TGMsim\nAxel Fox - Multimino\nDr Ocelot - Tetra Legends\n" ..
"Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" .. "Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" ..
"Mine - Tetra Online\nMrZ - Techmino\n" .. "Mine - Tetra Online\nMrZ - Techmino\nosk - TETR.IO\n" ..
"Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" .. "Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" ..
"Rin - Puzzle Trial\nsinefuse - stackfuse", "Rin - Puzzle Trial\nsinefuse - stackfuse",
320, 790 - offset 320, 790 - offset
@@ -66,7 +66,7 @@ function CreditsScene:render()
"CylinderKnot\neightsixfivezero\nEricICX\nGesomaru\n" .. "CylinderKnot\neightsixfivezero\nEricICX\nGesomaru\n" ..
"gizmo4487\nJBroms\nKirby703\nKitaru\n" .. "gizmo4487\nJBroms\nKirby703\nKitaru\n" ..
"M1ssing0\nMattMayuga\nMyPasswordIsWeak\n" .. "M1ssing0\nMattMayuga\nMyPasswordIsWeak\n" ..
"Nikki Karissa\nnim\noffwo\nOliver\nPineapple\npokemonfan1937\n" .. "Nikki Karissa\noffwo\nOliver\nPineapple\npokemonfan1937\n" ..
"Pyra Neoxi\nRDST64\nRocketLanterns\nRustyFoxxo\n" .. "Pyra Neoxi\nRDST64\nRocketLanterns\nRustyFoxxo\n" ..
"saphie\nShelleloch\nSimon\nstratus\nSuper302\n" .. "saphie\nShelleloch\nSimon\nstratus\nSuper302\n" ..
"switchpalacecorner\nterpyderp\nTetrian22\nTetro48\nThatCookie\n" .. "switchpalacecorner\nterpyderp\nTetrian22\nTetro48\nThatCookie\n" ..
@@ -75,7 +75,7 @@ function CreditsScene:render()
"Tetra Legends Discord\nTetra Online Discord\nMultimino Discord\n" .. "Tetra Legends Discord\nTetra Online Discord\nMultimino Discord\n" ..
"Hard Drop Discord\nRusty's Systemspace\nCambridge Discord\n" .. "Hard Drop Discord\nRusty's Systemspace\nCambridge Discord\n" ..
"And to you, the player!", "And to you, the player!",
320, 1040 - offset 320, 1060 - offset
) )
end end

View File

@@ -9,7 +9,6 @@ function GameScene:new(game_mode, ruleset, inputs)
self.retry_ruleset = ruleset self.retry_ruleset = ruleset
self.secret_inputs = inputs self.secret_inputs = inputs
self.game = game_mode(self.secret_inputs) self.game = game_mode(self.secret_inputs)
self.game.secret_inputs = inputs
self.ruleset = ruleset(self.game) self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset) self.game:initialize(self.ruleset)
self.inputs = { self.inputs = {
@@ -41,8 +40,6 @@ function GameScene:update()
self.game:update(inputs, self.ruleset) self.game:update(inputs, self.ruleset)
self.game.grid:update() self.game.grid:update()
DiscordRPC:update({ DiscordRPC:update({
details = self.game.rpc_details,
state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00" largeImageKey = "ingame-"..self.game:getBackground().."00"
}) })
end end

View File

@@ -13,12 +13,11 @@ ConfigScene.options = {
{"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}}, {"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
{"spawn_positions", "Spawn Positions", false, {"Per ruleset", "In field", "Out of field"}}, {"spawn_positions", "Spawn Positions", false, {"Per ruleset", "In field", "Out of field"}},
{"display_gamemode", "Debug Info", false, {"On", "Off"}}, {"display_gamemode", "Debug Info", false, {"On", "Off"}},
{"save_replay", "Save Replays", false, {"On", "Off"}},
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
{"diagonal_input", "Diagonal Input", false, {"On", "Off"}},
{"das_last_key", "DAS Last Key", false, {"Off", "On"}}, {"das_last_key", "DAS Last Key", false, {"Off", "On"}},
{"buffer_lock", "Buffer Drop Type", false, {"Off", "Hold", "Tap"}}, {"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
{"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}}, {"synchroes_allowed", "Synchroes", false, {"Per ruleset", "On", "Off"}},
{"diagonal_input", "Diagonal Input", false, {"On", "Off"}},
{"buffer_lock", "Buffer Drop Type", false, {"Off", "Hold", "Tap"}},
{"sfx_volume", "SFX", true, "sfxSlider"}, {"sfx_volume", "SFX", true, "sfxSlider"},
{"bgm_volume", "BGM", true, "bgmSlider"}, {"bgm_volume", "BGM", true, "bgmSlider"},
} }
@@ -34,8 +33,8 @@ function ConfigScene:new()
state = "Changing game settings", state = "Changing game settings",
}) })
self.sfxSlider = newSlider(165, 420, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20, knob="circle", track="roundrect"}) self.sfxSlider = newSlider(165, 400, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
self.bgmSlider = newSlider(465, 420, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"}) self.bgmSlider = newSlider(465, 400, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
end end
function ConfigScene:update() function ConfigScene:update()
@@ -59,7 +58,7 @@ function ConfigScene:render()
if not ConfigScene.options[self.highlight][3] then if not ConfigScene.options[self.highlight][3] then
love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22) love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
else else
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 362, 215, 33) love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 342, 215, 33)
end end
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
@@ -76,8 +75,8 @@ function ConfigScene:render()
love.graphics.setColor(1, 1, 1, 1) love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 365) love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 345)
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 365) love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 345)
love.graphics.setColor(1, 1, 1, 0.75) love.graphics.setColor(1, 1, 1, 0.75)
self.sfxSlider:draw() self.sfxSlider:draw()

View File

@@ -1,6 +1,6 @@
local ModeSelectScene = Scene:extend() local ModeSelectScene = Scene:extend()
ModeSelectScene.title = "Game Start" ModeSelectScene.title = "Freeplay"
current_mode = 1 current_mode = 1
current_ruleset = 1 current_ruleset = 1
@@ -108,6 +108,7 @@ function ModeSelectScene:render()
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left") love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
end end
end end
love.graphics.printf(game_modes[self.menu_state.mode].tagline, 5, 5, 600, "left")
end end
function ModeSelectScene:onInputPress(e) function ModeSelectScene:onInputPress(e)
@@ -187,4 +188,4 @@ function ModeSelectScene:changeRuleset(rel)
self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len) self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len)
end end
return ModeSelectScene return ModeSelectScene

View File

@@ -1,92 +0,0 @@
local Sequence = require 'tetris.randomizers.fixed_sequence'
local ReplayScene = Scene:extend()
ReplayScene.title = "Replay"
function ReplayScene:new(replay, game_mode, ruleset)
config.gamesettings = replay["gamesettings"]
love.math.setRandomSeed(replay["random_low"], replay["random_high"])
love.math.setRandomState(replay["random_state"])
self.retry_replay = replay
self.retry_mode = game_mode
self.retry_ruleset = ruleset
self.secret_inputs = replay["secret_inputs"]
self.game = game_mode(self.secret_inputs)
self.game.save_replay = false
self.ruleset = ruleset(self.game)
self.game:initialize(self.ruleset)
self.inputs = {
left=false,
right=false,
up=false,
down=false,
rotate_left=false,
rotate_left2=false,
rotate_right=false,
rotate_right2=false,
rotate_180=false,
hold=false,
}
self.paused = false
self.replay = deepcopy(replay)
self.replay_index = 1
DiscordRPC:update({
details = "Viewing a replay",
state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
function ReplayScene:update()
if love.window.hasFocus() and not self.paused then
self.inputs = self.replay["inputs"][self.replay_index]["inputs"]
self.replay["inputs"][self.replay_index]["frames"] = self.replay["inputs"][self.replay_index]["frames"] - 1
if self.replay["inputs"][self.replay_index]["frames"] == 0 and self.replay_index < table.getn(self.replay["inputs"]) then
self.replay_index = self.replay_index + 1
end
local input_copy = {}
for input, value in pairs(self.inputs) do
input_copy[input] = value
end
self.game:update(input_copy, self.ruleset)
self.game.grid:update()
DiscordRPC:update({
details = "Viewing a replay",
state = self.game.name,
largeImageKey = "ingame-"..self.game:getBackground().."00"
})
end
end
function ReplayScene:render()
self.game:draw(self.paused)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_3)
love.graphics.printf("REPLAY", 0, 0, 635, "right")
end
function ReplayScene:onInputPress(e)
if (
e.input == "menu_back" or
e.input == "menu_decide" or
e.input == "retry"
) then
self.game:onExit()
loadSave()
love.math.setRandomSeed(os.time())
scene = (
(e.input == "retry") and
ReplayScene(
self.retry_replay, self.retry_mode,
self.retry_ruleset, self.secret_inputs
) or ReplaySelectScene()
)
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
end
end
return ReplayScene

View File

@@ -1,222 +0,0 @@
local ReplaySelectScene = Scene:extend()
ReplaySelectScene.title = "Replays"
local binser = require 'libs.binser'
current_replay = 1
function ReplaySelectScene:new()
-- reload custom modules
initModules()
-- load replays
replays = {}
replay_file_list = love.filesystem.getDirectoryItems("replays")
for i=1,#replay_file_list do
local data = love.filesystem.read("replays/"..replay_file_list[i])
local new_replay = binser.deserialize(data)[1]
replays[#replays + 1] = new_replay
end
table.sort(replays, function(a, b)
return a["timestamp"] > b["timestamp"]
end)
self.display_error = false
if table.getn(replays) == 0 then
self.display_warning = true
current_replay = 1
else
self.display_warning = false
if current_replay > table.getn(replays) then
current_replay = 1
end
end
self.menu_state = {
replay = current_replay,
}
self.das = 0
DiscordRPC:update({
details = "In menus",
state = "Choosing a replay",
largeImageKey = "ingame-000"
})
end
function ReplaySelectScene:update()
switchBGM(nil) -- experimental
if self.das_up or self.das_down or self.das_left or self.das_right then
self.das = self.das + 1
else
self.das = 0
end
if self.das >= 15 then
local change = 0
if self.das_up then
change = -1
elseif self.das_down then
change = 1
elseif self.das_left then
change = -9
elseif self.das_right then
change = 9
end
self:changeOption(change)
self.das = self.das - 4
end
DiscordRPC:update({
details = "In menus",
state = "Choosing a replay",
largeImageKey = "ingame-000"
})
end
function ReplaySelectScene:render()
love.graphics.draw(
backgrounds[0],
0, 0, 0,
0.5, 0.5
)
-- Same graphic as mode select
--love.graphics.draw(misc_graphics["select_mode"], 20, 40)
love.graphics.setFont(font_3x5_4)
love.graphics.print("SELECT REPLAY", 20, 35)
if self.display_warning then
love.graphics.setFont(font_3x5_3)
love.graphics.printf(
"You have no replays.",
80, 200, 480, "center"
)
love.graphics.setFont(font_3x5_2)
love.graphics.printf(
"Come back to this menu after playing some games. " ..
"Press any button to return to the main menu.",
80, 250, 480, "center"
)
return
elseif self.display_error then
love.graphics.setFont(font_3x5_3)
love.graphics.printf(
"You are missing this mode or ruleset.",
80, 200, 480, "center"
)
love.graphics.setFont(font_3x5_2)
love.graphics.printf(
"Come back after getting the proper mode or ruleset. " ..
"Press any button to return to the main menu.",
80, 250, 480, "center"
)
return
end
love.graphics.setColor(1, 1, 1, 0.5)
love.graphics.rectangle("fill", 3, 258, 634, 22)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setFont(font_3x5_2)
for idx, replay in ipairs(replays) do
if(idx >= self.menu_state.replay-9 and idx <= self.menu_state.replay+9) then
local display_string = os.date("%c", replay["timestamp"]).." - "..replay["mode"].." - "..replay["ruleset"]
if replay["level"] ~= nil then
display_string = display_string.." - Level: "..replay["level"]
end
if replay["timer"] ~= nil then
display_string = display_string.." - Time: "..formatTime(replay["timer"])
end
if #display_string > 75 then
display_string = display_string:sub(1, 75) .. "..."
end
love.graphics.printf(display_string, 6, (260 - 20*(self.menu_state.replay)) + 20 * idx, 640, "left")
end
end
end
function ReplaySelectScene:onInputPress(e)
if (self.display_warning or self.display_error) and e.input then
scene = TitleScene()
elseif e.type == "wheel" then
if e.y ~= 0 then
self:changeOption(-e.y)
end
elseif e.input == "menu_decide" or e.scancode == "return" then
current_replay = self.menu_state.replay
-- Same as mode decide
playSE("mode_decide")
-- Get game mode and ruleset
local mode
local rules
for key, value in pairs(game_modes) do
if value.name == replays[self.menu_state.replay]["mode"] then
mode = value
break
end
end
for key, value in pairs(rulesets) do
if value.name == replays[self.menu_state.replay]["ruleset"] then
rules = value
break
end
end
if mode == nil or rules == nil then
self.display_error = true
return
end
-- TODO compare replay versions to current versions for Cambridge, ruleset, and mode
scene = ReplayScene(
replays[self.menu_state.replay],
mode,
rules
)
elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1)
self.das_up = true
self.das_down = nil
self.das_left = nil
self.das_right = nil
elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1)
self.das_down = true
self.das_up = nil
self.das_left = nil
self.das_right = nil
elseif e.input == "left" or e.scancode == "left" then
self:changeOption(-9)
self.das_left = true
self.das_right = nil
self.das_up = nil
self.das_down = nil
elseif e.input == "right" or e.scancode == "right" then
self:changeOption(9)
self.das_right = true
self.das_left = nil
self.das_up = nil
self.das_down = nil
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene()
end
end
function ReplaySelectScene:onInputRelease(e)
if e.input == "up" or e.scancode == "up" then
self.das_up = nil
elseif e.input == "down" or e.scancode == "down" then
self.das_down = nil
elseif e.input == "right" or e.scancode == "right" then
self.das_right = nil
elseif e.input == "left" or e.scancode == "left" then
self.das_left = nil
end
end
function ReplaySelectScene:changeOption(rel)
local len = table.getn(replays)
self.menu_state.replay = Mod1(self.menu_state.replay + rel, len)
playSE("cursor")
end
return ReplaySelectScene

View File

@@ -20,7 +20,7 @@ function SettingsScene:new()
self.menu_state = 1 self.menu_state = 1
DiscordRPC:update({ DiscordRPC:update({
details = "In settings", details = "In settings",
state = settingsidle[love.math.random(#settingsidle)], state = settingsidle[math.random(#settingsidle)],
largeImageKey = "settings", largeImageKey = "settings",
}) })
end end

View File

@@ -5,7 +5,7 @@ TitleScene.restart_message = false
local main_menu_screens = { local main_menu_screens = {
ModeSelectScene, ModeSelectScene,
ReplaySelectScene, ChallengeSelectScene,
SettingsScene, SettingsScene,
CreditsScene, CreditsScene,
ExitScene, ExitScene,
@@ -35,6 +35,21 @@ local mainmenuidle = {
"This is probably the longest RPC string out of every possible RPC string that can be displayed." "This is probably the longest RPC string out of every possible RPC string that can be displayed."
} }
local menusplash = {
"Welcome to Cambridge!",
"Get ready to put the block!",
"Also try Master of Blocks!",
"Also try Shiromino!",
"1 year in the making!",
"haileyjunk!",
"WOOOOAAAAAHHH!!!!!"
}
local currentSplash = menusplash[math.random(#menusplash)]
local now = os.date("t")
showDebugKeys = false
function TitleScene:new() function TitleScene:new()
self.main_menu_state = 1 self.main_menu_state = 1
self.frames = 0 self.frames = 0
@@ -44,10 +59,16 @@ function TitleScene:new()
self.text_flag = false self.text_flag = false
DiscordRPC:update({ DiscordRPC:update({
details = "In menus", details = "In menus",
state = mainmenuidle[love.math.random(#mainmenuidle)], state = mainmenuidle[math.random(#mainmenuidle)],
largeImageKey = "icon2", largeImageKey = "1year",
largeImageText = version largeImageText = version.." | Thanks for 1 year!"
}) })
if now.month == 12 then
DiscordRPC:update({
largeImageKey = "snow"
})
end
end end
function TitleScene:update() function TitleScene:update()
@@ -60,42 +81,27 @@ function TitleScene:update()
else self.y_offset = 310 - self.frames end else self.y_offset = 310 - self.frames end
end end
local block_offsets = {
{color = "M", x = 0, y = 0},
{color = "G", x = 32, y = 0},
{color = "Y", x = 64, y = 0},
{color = "B", x = 0, y = 32},
{color = "O", x = 0, y = 64},
{color = "C", x = 32, y = 64},
{color = "R", x = 64, y = 64}
}
function TitleScene:render() function TitleScene:render()
love.graphics.setFont(font_3x5_4) love.graphics.setFont(font_3x5_4)
love.graphics.setColor(1, 1, 1, 1 - self.snow_bg_opacity) love.graphics.setColor(1, 1, 1, 1 - self.snow_bg_opacity)
--[[
love.graphics.draw( love.graphics.draw(
backgrounds["title_no_icon"], -- title, title_night backgrounds["title"],
0, 0, 0,
0.5, 0.5
)
]]
love.graphics.draw(
backgrounds["title_night"],
0, 0, 0, 0, 0, 0,
0.5, 0.5 0.5, 0.5
) )
-- 490, 192
for _, b in ipairs(block_offsets) do
love.graphics.draw(
blocks["2tie"][b.color],
490 + b.x, 192 + b.y, 0,
2, 2
)
end
--[[
love.graphics.draw( love.graphics.draw(
misc_graphics["icon"], misc_graphics["icon"],
490, 192, 0, 460, 170, 0,
2, 2 2, 2
) )
]] love.graphics.printf(currentSplash, 390, 280, 320, "center", 0, 0.75, 0.75)
--love.graphics.printf("Thanks for 1 year!", 430, 280, 160, "center")
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
love.graphics.setColor(1, 1, 1, self.snow_bg_opacity) love.graphics.setColor(1, 1, 1, self.snow_bg_opacity)
@@ -122,6 +128,11 @@ function TitleScene:render()
for i, screen in pairs(main_menu_screens) do for i, screen in pairs(main_menu_screens) do
love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left") love.graphics.printf(screen.title, 40, 280 + 20 * i, 120, "left")
end end
if showDebugKeys then
love.graphics.print("DEBUG KEYS\n\nF3+S: Get new splash message\nF3+R: Restart\nF3+I: Toggle this")
end
end end
function TitleScene:changeOption(rel) function TitleScene:changeOption(rel)
@@ -129,7 +140,10 @@ function TitleScene:changeOption(rel)
self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1 self.main_menu_state = (self.main_menu_state + len + rel - 1) % len + 1
end end
function TitleScene:onInputPress(e) function TitleScene:onInputPress(e)
local debugkey = love.keyboard.isDown("f3")
if e.input == "menu_decide" or e.scancode == "return" then if e.input == "menu_decide" or e.scancode == "return" then
playSE("main_decide") playSE("main_decide")
scene = main_menu_screens[self.main_menu_state]() scene = main_menu_screens[self.main_menu_state]()
@@ -141,6 +155,27 @@ 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()
-- small debug feature, press f3+s to get a new splash message
elseif e.scancode == "s" then
if debugkey then
currentSplash = menusplash[math.random(#menusplash)]
playSE("main_decide")
end
elseif e.scancode == "r" then
if debugkey then
love.event.quit("restart")
end
elseif e.scancode == "i" then
if debugkey then
if showDebugKeys then
showDebugKeys = false
else
showDebugKeys = true
end
end
-- no winter easter egg for now
--[[
else else
self.text = self.text .. (e.scancode or "") self.text = self.text .. (e.scancode or "")
if self.text == "ffffff" then if self.text == "ffffff" then
@@ -149,6 +184,7 @@ function TitleScene:onInputPress(e)
largeImageKey = "snow" largeImageKey = "snow"
}) })
end end
]]
end end
end end

View File

@@ -1 +0,0 @@
dist\win32\love.exe .

View File

@@ -1 +0,0 @@
dist\windows\love.exe .

View File

@@ -0,0 +1,26 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7Randomizer = require 'tetris.randomizers.bag7noI'
local MarathonGF = require 'tetris.modes.marathon_gf'
local TetrsChallenge = MarathonGF:extend()
TetrsChallenge.name = "Tetrs"
TetrsChallenge.hash = "Tetrs"
TetrsChallenge.mode = "MarathonGF"
TetrsChallenge.ruleset = "Standard"
TetrsChallenge.tagline = "Where's the long bar? Seriously, I can't find it."
TetrsChallenge.description = "Complete a 150-line Marathon...without the I piece!"
function TetrsChallenge:new()
TetrsChallenge.super:new()
self.randomizer = Bag7Randomizer()
self.next_queue_length = 6
end
return TetrsChallenge

View File

@@ -0,0 +1,23 @@
-- currently you need to require and extend the gamemode you're making a challenge out of
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer'
local BagRandomizer = require 'tetris.randomizers.bag'
local MarathonGF = require 'tetris.modes.marathon_gf'
local Challenge = GameMode:extend()
Challenge.name = "A really cool challenge name"
Challenge.hash = ""
Challenge.mode = ""
Challenge.ruleset = ""
Challenge.tagline = "Are you up for this challenge?"
Challenge.description = "Complete a mode with a specific ruleset and idk they did some other stupid things too lol"
return Challenge

View File

@@ -0,0 +1,26 @@
require 'funcs'
local GameMode = require 'tetris.modes.gamemode'
local Piece = require 'tetris.components.piece'
local Grid = require 'tetris.components.grid'
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7Randomizer = require 'tetris.randomizers.bag7noI'
local MarathonGF = require 'tetris.modes.marathon_gf'
local TetrsChallenge = MarathonGF:extend()
TetrsChallenge.name = "Another mode"
TetrsChallenge.hash = "Tetrs2"
TetrsChallenge.mode = "MarathonGF"
TetrsChallenge.ruleset = "Standard"
TetrsChallenge.tagline = "Hey look! The feature works!"
TetrsChallenge.description = "This is just a clone of Tetrs to test out the challenge details feature."
function TetrsChallenge:new()
TetrsChallenge.super:new()
self.randomizer = Bag7Randomizer()
self.next_queue_length = 6
end
return TetrsChallenge

View File

@@ -182,19 +182,6 @@ function Grid:clearSpecificRow(row)
end end
end end
function Grid:clearBlock(x, y)
self.grid[x+1][y+1] = empty
end
function Grid:clearBottomRows(num)
local old_isRowFull = self.isRowFull
self.isRowFull = function(self, row)
return row >= self.height + 1 - num
end
self:clearClearedRows()
self.isRowFull = old_isRowFull
end
function Grid:applyPiece(piece) function Grid:applyPiece(piece)
if piece.big then if piece.big then
self:applyBigPiece(piece) self:applyBigPiece(piece)

View File

@@ -7,7 +7,6 @@ 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.randomizer'
local BagRandomizer = require 'tetris.randomizers.bag' local BagRandomizer = require 'tetris.randomizers.bag'
local binser = require 'libs.binser'
local GameMode = Object:extend() local GameMode = Object:extend()
@@ -16,12 +15,7 @@ GameMode.hash = ""
GameMode.tagline = "" GameMode.tagline = ""
GameMode.rollOpacityFunction = function(age) return 0 end GameMode.rollOpacityFunction = function(age) return 0 end
function GameMode:new() function GameMode:new(secret_inputs)
self.replay_inputs = {}
self.random_low, self.random_high = love.math.getRandomSeed()
self.random_state = love.math.getRandomState()
self.save_replay = config.gamesettings.save_replay == 1
self.grid = Grid(10, 24) self.grid = Grid(10, 24)
self.randomizer = Randomizer() self.randomizer = Randomizer()
self.piece = nil self.piece = nil
@@ -120,64 +114,8 @@ function GameMode:initialize(ruleset)
self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock] self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock]
end end
function GameMode:saveReplay()
-- Save replay.
local replay = {}
replay["inputs"] = self.replay_inputs
replay["random_low"] = self.random_low
replay["random_high"] = self.random_high
replay["random_state"] = self.random_state
replay["mode"] = self.name
replay["ruleset"] = self.ruleset.name
replay["timer"] = self.frames
replay["score"] = self.score
replay["level"] = self.level
replay["lines"] = self.lines
replay["gamesettings"] = config.gamesettings
replay["secret_inputs"] = self.secret_inputs
replay["timestamp"] = os.time()
if love.filesystem.getInfo("replays") == nil then
love.filesystem.createDirectory("replays")
end
local replay_files = love.filesystem.getDirectoryItems("replays")
-- Select replay filename that doesn't collide with an existing one
local replay_number = 0
local collision = true
while collision do
collision = false
replay_number = replay_number + 1
for key, file in pairs(replay_files) do
if file == replay_number..".crp" then
collision = true
break
end
end
end
love.filesystem.write("replays/"..replay_number..".crp", binser.serialize(replay))
end
function GameMode:addReplayInput(inputs)
-- check if inputs have changed since last frame
if not equals(self.prev_inputs, inputs) then
-- insert new inputs into replay inputs table
local new_inputs = {}
new_inputs["inputs"] = {}
new_inputs["frames"] = 1
for key, value in pairs(inputs) do
new_inputs["inputs"][key] = value
end
self.replay_inputs[#self.replay_inputs + 1] = new_inputs
else
-- add 1 to input frame counter
self.replay_inputs[#self.replay_inputs]["frames"] = self.replay_inputs[#self.replay_inputs]["frames"] + 1
end
end
function GameMode:update(inputs, ruleset) function GameMode:update(inputs, ruleset)
if self.game_over or self.completed then if self.game_over or self.completed then
if self.save_replay and self.game_over_frames == 0 then
self:saveReplay()
end
self.game_over_frames = self.game_over_frames + 1 self.game_over_frames = self.game_over_frames + 1
return return
end end
@@ -191,8 +129,6 @@ function GameMode:update(inputs, ruleset)
end end
end end
if self.save_replay then self:addReplayInput(inputs) end
-- advance one frame -- advance one frame
if self:advanceOneFrame(inputs, ruleset) == false then return end if self:advanceOneFrame(inputs, ruleset) == false then return end
@@ -207,7 +143,7 @@ function GameMode:update(inputs, ruleset)
) then ) then
self:onAttemptPieceRotate(self.piece, self.grid) self:onAttemptPieceRotate(self.piece, self.grid)
end end
if self.piece == nil then if self.piece == nil then
self:processDelays(inputs, ruleset) self:processDelays(inputs, ruleset)
else else
@@ -307,7 +243,7 @@ function GameMode:update(inputs, ruleset)
if self.immobile_spin_bonus and if self.immobile_spin_bonus and
self.piece.last_rotated and ( self.piece.last_rotated and (
self.piece:isDropBlocked(self.grid) and self.piece:isDropBlocked(self.grid) and
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and
self.piece:isMoveBlocked(self.grid, { x=0, y=-1 }) self.piece:isMoveBlocked(self.grid, { x=0, y=-1 })
) then ) then
@@ -315,7 +251,7 @@ function GameMode:update(inputs, ruleset)
end end
self.grid:applyPiece(self.piece) self.grid:applyPiece(self.piece)
-- mark squares (can be overridden) -- mark squares (can be overridden)
if self.square_mode then if self.square_mode then
self.squares = self.squares + self.grid:markSquares() self.squares = self.squares + self.grid:markSquares()
@@ -376,7 +312,7 @@ function GameMode:onAttemptPieceRotate(piece, grid) end
function GameMode:onPieceMove(piece, grid, dx) end function GameMode:onPieceMove(piece, grid, dx) end
function GameMode:onPieceRotate(piece, grid, drot) end function GameMode:onPieceRotate(piece, grid, drot) end
function GameMode:onPieceDrop(piece, grid, dy) end function GameMode:onPieceDrop(piece, grid, dy) end
function GameMode:onPieceLock(piece, cleared_row_count) function GameMode:onPieceLock(piece, cleared_row_count)
playSE("lock") playSE("lock")
end end
@@ -398,16 +334,7 @@ end
function GameMode:onGameOver() function GameMode:onGameOver()
switchBGM(nil) switchBGM(nil)
local alpha = 0 love.graphics.setColor(0, 0, 0, 1 - 2 ^ (-self.game_over_frames / 30))
local animation_length = 120
if self.game_over_frames < animation_length then
-- Show field for a bit, then fade out.
alpha = math.pow(2048, self.game_over_frames/animation_length - 1)
elseif self.game_over_frames < 2 * animation_length then
-- Keep field hidden for a short time, then pop it back in (for screenshots).
alpha = 1
end
love.graphics.setColor(0, 0, 0, alpha)
love.graphics.rectangle( love.graphics.rectangle(
"fill", 64, 80, "fill", 64, 80,
16 * self.grid.width, 16 * (self.grid.height - 4) 16 * self.grid.width, 16 * (self.grid.height - 4)
@@ -603,8 +530,6 @@ function GameMode:onEnterOrHold(inputs, ruleset)
if not self.grid:canPlacePiece(self.piece) then if not self.grid:canPlacePiece(self.piece) then
self.game_over = true self.game_over = true
return return
elseif self.piece:isDropBlocked(self.grid) then
playSE("bottom")
end end
ruleset:dropPiece( ruleset:dropPiece(
inputs, self.piece, self.grid, self:getGravity(), inputs, self.piece, self.grid, self:getGravity(),
@@ -653,6 +578,10 @@ function GameMode:initializeNextPiece(
self.piece_soft_locked = false self.piece_soft_locked = false
self.buffer_hard_drop = false self.buffer_hard_drop = false
self.buffer_soft_drop = false self.buffer_soft_drop = false
if self.piece:isDropBlocked(self.grid) and
self.grid:canPlacePiece(self.piece) then
playSE("bottom")
end
if generate_next_piece == nil then if generate_next_piece == nil then
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))
@@ -671,18 +600,11 @@ function GameMode:getHighScoreData()
end end
function GameMode:animation(x, y, skin, colour) function GameMode:animation(x, y, skin, colour)
-- Animation progress where 0 = start and 1 = end
local progress = 1
if self.last_lcd ~= 0 then
progress = (self.last_lcd - self.lcd) / self.last_lcd
end
-- Convert progress through the animation into an alpha value, with easing
local alpha = 1 - progress ^ 2
return { return {
1, 1, 1, 1, 1, 1,
alpha, -0.25 + 1.25 * (self.lcd / self.last_lcd),
skin, colour, skin, colour,
48 + x * 16, y * 16 48 + x * 16, y * 16
} }
end end
@@ -695,55 +617,7 @@ function GameMode:drawLineClearAnimation()
-- params: block x, y, skin, colour -- params: block x, y, skin, colour
-- returns: table with RGBA, skin, colour, x, y -- returns: table with RGBA, skin, colour, x, y
-- Quadratic Fadeout (default) -- Fadeout (default)
--[[
function animation(x, y, skin, colour)
-- Animation progress where 0 = start and 1 = end
local progress = 1
if self.last_lcd ~= 0 then
progress = (self.last_lcd - self.lcd) / self.last_lcd
end
-- Convert progress through the animation into an alpha value, with easing
local alpha = 1 - progress ^ 2
return {
1, 1, 1,
alpha,
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- Flashy Fadeout
--[[
function animation(x, y, skin, colour)
-- Animation progress where 0 = start and 1 = end
local progress = 1
if self.last_lcd ~= 0 then
progress = (self.last_lcd - self.lcd) / self.last_lcd
end
-- Change this number to change "bounciness"
local bounce = 13
-- Convert progress through the animation into an alpha value
local alpha = 0
-- Cutoff is arbitrary: corresponds to level 500 in Marathon A2
if self.last_lcd > 25 then
-- Goes up and down: looks better when animation is long
alpha = 1 - (bounce * progress^3 - 1.5 * bounce * progress^2 + (0.5 * bounce + 1) * progress)
else
-- Always decreasing: looks better when animation is short
alpha = 1 - progress * progress
end
return {
1, 1, 1,
alpha,
skin, colour,
48 + x * 16, y * 16
}
end
--]]
-- Fadeout
--[[ --[[
function animation(x, y, skin, colour) function animation(x, y, skin, colour)
return { return {
@@ -854,8 +728,8 @@ function GameMode:drawNextQueue(ruleset)
if self.hold_queue ~= nil and self.enable_hold then if self.hold_queue ~= nil and self.enable_hold then
self:setHoldOpacity() self:setHoldOpacity()
drawPiece( drawPiece(
self.hold_queue.shape, self.hold_queue.shape,
self.hold_queue.skin, self.hold_queue.skin,
ruleset.block_offsets[self.hold_queue.shape][self.hold_queue.orientation], ruleset.block_offsets[self.hold_queue.shape][self.hold_queue.orientation],
-16, -32 -16, -32
) )
@@ -954,7 +828,7 @@ end
function GameMode:drawSectionTimesWithSplits(current_section, section_limit) function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
section_limit = section_limit or math.huge section_limit = section_limit or math.huge
local section_x = 440 local section_x = 440
local split_x = 530 local split_x = 530
@@ -969,7 +843,7 @@ function GameMode:drawSectionTimesWithSplits(current_section, section_limit)
love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left") love.graphics.printf(formatTime(split_time), split_x, 40 + 20 * section, 90, "left")
end end
end end
if (current_section <= section_limit) then if (current_section <= section_limit) then
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left") love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left") love.graphics.printf(formatTime(self.frames), split_x, 40 + 20 * current_section, 90, "left")
@@ -1032,7 +906,7 @@ function GameMode:drawCustom() end
function GameMode:drawIfPaused() function GameMode:drawIfPaused()
love.graphics.setFont(font_3x5_3) love.graphics.setFont(font_3x5_3)
love.graphics.printf("PAUSED!", 64, 160, 160, "center") love.graphics.printf("GAME PAUSED!", 64, 160, 160, "center")
end end
-- transforms specified in here will transform the whole screen -- transforms specified in here will transform the whole screen

View File

@@ -309,7 +309,7 @@ local mroll_points = {10, 20, 30, 100}
local grade_conversion = { local grade_conversion = {
[0] = 0, [0] = 0,
1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
7, 8, 8, 8, 9, 9, 9, 10, 11, 12, 7, 8, 8, 8, 9, 9, 9, 10, 11, 12, 12,
12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17 17
} }
@@ -354,7 +354,8 @@ function MarathonA3Game:getAggregateGrade()
return math.min( return math.min(
self.section_cool_grade + self.section_cool_grade +
math.floor(self.roll_points / 100) + math.floor(self.roll_points / 100) +
grade_conversion[self.grade] grade_conversion[self.grade],
self.roll_frames > 3238 and 32 or 31
) )
end end

View File

@@ -28,6 +28,7 @@ function PhantomManiaGame:new()
self.combo = 1 self.combo = 1
self.tetrises = 0 self.tetrises = 0
self.section_tetrises = {[0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} self.section_tetrises = {[0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
self.section_req = true
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
end end
@@ -119,6 +120,11 @@ function PhantomManiaGame:onLineClear(cleared_row_count)
self.level = 999 self.level = 999
end end
self.clear = true self.clear = true
for i = 0, 9 do
if self.section_tetrises[i] < (i == 9 and 1 or 2) then
self.section_req = false
end
end
else else
self.level = new_level self.level = new_level
end end
@@ -169,10 +175,6 @@ local function getLetterGrade(level, clear)
end end
end end
function PhantomManiaGame:qualifiesForGM()
return true
end
function PhantomManiaGame:drawScoringInfo() function PhantomManiaGame:drawScoringInfo()
PhantomManiaGame.super.drawScoringInfo(self) PhantomManiaGame.super.drawScoringInfo(self)
@@ -193,7 +195,7 @@ function PhantomManiaGame:drawScoringInfo()
if getLetterGrade(self.level, self.clear) ~= "" then if getLetterGrade(self.level, self.clear) ~= "" then
if self.roll_frames > 1982 then love.graphics.setColor(1, 0.5, 0, 1) if self.roll_frames > 1982 then love.graphics.setColor(1, 0.5, 0, 1)
elseif self.level == 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end elseif self.level == 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
if self.level == 999 and self:qualifiesForGM() then if self.level == 999 and self.section_req and self.tetrises >= 31 then
love.graphics.printf("GM", text_x, 140, 90, "left") love.graphics.printf("GM", text_x, 140, 90, "left")
else else
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")

View File

@@ -202,13 +202,13 @@ end
local cool_cutoffs = { local cool_cutoffs = {
frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36), frameTime(0,36),
frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,30),
frameTime(0,30), frameTime(0,30), frameTime(0,30), frameTime(0,27), frameTime(0,27), frameTime(0,27),
} }
local regret_cutoffs = { local regret_cutoffs = {
frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50),
frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40),
frameTime(0,42), frameTime(0,42), frameTime(0,42), frameTime(0,35), frameTime(0,35), frameTime(0,35),
} }
function PhantomMania2Game:updateSectionTimes(old_level, new_level) function PhantomMania2Game:updateSectionTimes(old_level, new_level)

View File

@@ -13,14 +13,4 @@ function PhantomManiaNGame:new()
self.enable_hold = true self.enable_hold = true
end end
function PhantomManiaNGame:qualifiesForGM()
if self.tetrises < 31 then return false end
for i = 0, 9 do
if self.section_tetrises[i] < (i == 9 and 1 or 2) then
return false
end
end
return true
end
return PhantomManiaNGame return PhantomManiaNGame

View File

@@ -19,7 +19,7 @@ function SurvivalA2Game:new()
self.roll_frames = 0 self.roll_frames = 0
self.combo = 1 self.combo = 1
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.SGnames = { self.SGnames = {
"9", "8", "7", "6", "5", "4", "3", "2", "1", "9", "8", "7", "6", "5", "4", "3", "2", "1",
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
@@ -101,7 +101,6 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
self.clear = true self.clear = true
if new_level < 999 then if new_level < 999 then
self.game_over = true self.game_over = true
return
end end
end end
self.level = new_level self.level = new_level

View File

@@ -13,7 +13,7 @@ function BagRandomizer:generatePiece()
table.insert(self.bag, v) table.insert(self.bag, v)
end end
end end
local x = love.math.random(table.getn(self.bag)) local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x) return table.remove(self.bag, x)
end end

View File

@@ -10,7 +10,7 @@ function Bag7Randomizer:generatePiece()
if next(self.bag) == nil then if next(self.bag) == nil then
self.bag = {"I", "J", "L", "O", "S", "T", "Z"} self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
end end
local x = love.math.random(table.getn(self.bag)) local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x) return table.remove(self.bag, x)
end end

View File

@@ -0,0 +1,19 @@
-- for the pre-packaged/example challenge tetrs
local Randomizer = require 'tetris.randomizers.randomizer'
local Bag7NoIRandomizer = Randomizer:extend()
function Bag7NoIRandomizer:initialize()
self.bag = {"J", "L", "O", "S", "T", "Z"}
end
function Bag7NoIRandomizer:generatePiece()
if next(self.bag) == nil then
self.bag = {"J", "L", "O", "S", "T", "Z"}
end
local x = math.random(table.getn(self.bag))
return table.remove(self.bag, x)
end
return Bag7NoIRandomizer

View File

@@ -6,7 +6,7 @@ function Bag7NoSZOStartRandomizer:shuffleBag()
local b = self.bag local b = self.bag
local ln = #b local ln = #b
for i = 1, ln do for i = 1, ln do
local j = love.math.random(i, ln) local j = math.random(i, ln)
b[i], b[j] = b[j], b[i] b[i], b[j] = b[j], b[i]
end end
end end

View File

@@ -8,13 +8,8 @@ function Sequence:initialize()
end end
function Sequence:generatePiece() function Sequence:generatePiece()
local piece local piece = string.sub(self.sequence, self.counter + 1, self.counter + 1)
if type(self.sequence) == "string" then self.counter = (self.counter + 1) % string.len(self.sequence)
piece = string.sub(self.sequence, self.counter + 1, self.counter + 1)
else
piece = self.sequence[self.counter + 1]
end
self.counter = (self.counter + 1) % (#self.sequence)
return piece return piece
end end

View File

@@ -10,11 +10,11 @@ end
function History4RollsRandomizer:generatePiece() function History4RollsRandomizer:generatePiece()
if self.first then if self.first then
self.first = false self.first = false
return self:updateHistory(({"L", "J", "I", "T"})[love.math.random(4)]) return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
else else
local shapes = {"I", "J", "L", "O", "S", "T", "Z"} local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
for i = 1, 4 do for i = 1, 4 do
local x = love.math.random(7) local x = math.random(7)
if not inHistory(shapes[x], self.history) or i == 4 then if not inHistory(shapes[x], self.history) or i == 4 then
return self:updateHistory(shapes[x]) return self:updateHistory(shapes[x])
end end

View File

@@ -10,11 +10,11 @@ end
function History6RollsRandomizer:generatePiece() function History6RollsRandomizer:generatePiece()
if self.first then if self.first then
self.first = false self.first = false
return self:updateHistory(({"L", "J", "I", "T"})[love.math.random(4)]) return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
else else
local shapes = {"I", "J", "L", "O", "S", "T", "Z"} local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
for i = 1, 6 do for i = 1, 6 do
local x = love.math.random(7) local x = math.random(7)
if not inHistory(shapes[x], self.history) or i == 6 then if not inHistory(shapes[x], self.history) or i == 6 then
return self:updateHistory(shapes[x]) return self:updateHistory(shapes[x])
end end

View File

@@ -28,12 +28,12 @@ end
function History6Rolls35PoolRandomizer:generatePiece() function History6Rolls35PoolRandomizer:generatePiece()
local index, x local index, x
if self.first then if self.first then
index = love.math.random(20) index = math.random(20)
x = self.pool[index] x = self.pool[index]
self.first = false self.first = false
else else
for i = 1, 6 do for i = 1, 6 do
index = love.math.random(#self.pool) index = math.random(#self.pool)
x = self.pool[index] x = self.pool[index]
if not inHistory(x, self.history) or i == 6 then if not inHistory(x, self.history) or i == 6 then
break break

View File

@@ -16,7 +16,7 @@ function Randomizer:initialize()
end end
function Randomizer:generatePiece() function Randomizer:generatePiece()
return self.possible_pieces[love.math.random(7)] return self.possible_pieces[math.random(7)]
end end
return Randomizer return Randomizer

View File

@@ -1,49 +0,0 @@
local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.arika_ace2'
local ARS = Ruleset:extend()
ARS.name = "ARS-X"
ARS.hash = "ArikaEXP"
ARS.MANIPULATIONS_MAX = 24
ARS.ROTATIONS_MAX = 12
function ARS:onPieceCreate(piece, grid)
piece.manipulations = 0
piece.rotations = 0
piece.lowest_y = -math.huge
end
function ARS:checkNewLow(piece)
for _, block in pairs(piece:getBlockOffsets()) do
local y = piece.position.y + block.y
if y > piece.lowest_y then
piece.manipulations = 0
piece.rotations = 0
piece.lowest_y = y
end
end
end
function ARS:onPieceMove(piece, grid)
piece.lock_delay = 0 -- move reset
if piece:isDropBlocked(grid) then
piece.manipulations = piece.manipulations + 1
if piece.manipulations >= ARS.MANIPULATIONS_MAX then
piece.locked = true
end
end
end
function ARS:onPieceRotate(piece, grid, upward)
piece.lock_delay = 0 -- rotate reset
if upward or piece:isDropBlocked(grid) then
piece.rotations = piece.rotations + 1
if piece.rotations >= ARS.ROTATIONS_MAX and piece:isDropBlocked(grid) then
piece.locked = true
end
end
end
return ARS

View File

@@ -1,5 +1,5 @@
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.standard_ti' local Ruleset = require 'tetris.rulesets.ti_srs'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()

View File

@@ -209,7 +209,7 @@ function Ruleset:initializePiece(
local colours local colours
if table.equalvalues( if table.equalvalues(
table.keys(self.colourscheme), {"I", "J", "L", "O", "S", "T", "Z"} self.colourscheme, {"I", "J", "L", "O", "S", "T", "Z"}
) then ) then
colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour] colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
else else

View File

@@ -1,5 +1,5 @@
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.standard_ace' local Ruleset = require 'tetris.rulesets.standard_exp'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
@@ -33,8 +33,8 @@ SRS.wallkicks_line = {
}, },
}; };
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid) function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
local kicks local kicks
if piece.shape == "O" then if piece.shape == "O" then
return return
@@ -69,12 +69,6 @@ function SRS:checkNewLow(piece)
end end
end end
function SRS:onPieceCreate(piece, grid)
piece.manipulations = 0
piece.rotations = 0
piece.lowest_y = -math.huge
end
function SRS:onPieceDrop(piece, grid) function SRS:onPieceDrop(piece, grid)
self:checkNewLow(piece) self:checkNewLow(piece)
if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then if piece.manipulations >= self.MANIPULATIONS_MAX and piece:isDropBlocked(grid) then

View File

@@ -1,5 +1,5 @@
local Piece = require 'tetris.components.piece' local Piece = require 'tetris.components.piece'
local Ruleset = require 'tetris.rulesets.standard_ti' local Ruleset = require 'tetris.rulesets.arika_srs'
local SRS = Ruleset:extend() local SRS = Ruleset:extend()
@@ -27,6 +27,8 @@ 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
--piece.manipulations = 0
--piece.rotations = 0
piece.lowest_y = y piece.lowest_y = y
end end
end end