mirror of
https://github.com/SashLilac/cambridge.git
synced 2025-05-13 20:21:25 -05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b9740768b | ||
|
|
0e65b39395 | ||
|
|
ca04333bb7 | ||
|
|
80c7c99bd7 | ||
|
|
717afbebf6 | ||
|
|
7227085f84 | ||
|
|
c40392f00f | ||
|
|
001c8f0ea8 | ||
|
|
7fa0e60145 | ||
|
|
901f7f2d12 | ||
|
|
35f4aea67d | ||
|
|
7deaa5ab92 |
57
README.md
57
README.md
@@ -1,35 +1,33 @@
|
||||

|
||||
|
||||
Cambridge
|
||||
=========
|
||||
|
||||
Welcome to Cambridge, the next open-source falling-block game engine!
|
||||
|
||||
This fork is written and maintained exclusively by [SashLilac](https://github.com/SashLilac) and [Oshisaure](https://github.com/oshisaure)!
|
||||
|
||||
Join our Discord server for help and a welcoming community! https://discord.gg/mteMJw4
|
||||
Installation instructions
|
||||
-------------------------
|
||||
|
||||
Credits
|
||||
-------
|
||||
Pre-built releases are available on the releases page.
|
||||
|
||||
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for being my co-dev!
|
||||
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||
- [The Absolute Plus](https://discord.gg/6Gf2awJ) for being another source of motivation!
|
||||
- [joezeng](https://github.com/joezeng) for the original project.
|
||||
### Windows
|
||||
|
||||
The following people in no particular order also helped with the project:
|
||||
- [Hailey](https://github.com/haileylgbt)
|
||||
- CylinderKnot
|
||||
- MarkGamed7794
|
||||
- [Mizu](https://github.com/rexxt)
|
||||
- MattMayuga
|
||||
- Kitaru
|
||||
- switchpalacecorner
|
||||
Unzip the exe file and run it directly. All assets are currently bundled inside the executable.
|
||||
|
||||

|
||||
### macOS
|
||||
|
||||
Playing the game
|
||||
----------------
|
||||
For the time being, the file `cambridge.love` only works on the command line. Install `love` with [https://brew.sh/](Homebrew), and run:
|
||||
|
||||
$ love cambridge.love
|
||||
|
||||
### Linux
|
||||
|
||||
Same as macOS, except install `love` with your favourite package manager.
|
||||
|
||||
|
||||
Running from source
|
||||
-------------------
|
||||
|
||||
If you want the bleeding-edge release, you can also clone the code straight from this repository.
|
||||
|
||||
### macOS, Linux
|
||||
|
||||
@@ -37,9 +35,7 @@ If you haven't already, install `love` with your favourite package manager (Home
|
||||
|
||||
Clone the repository in git:
|
||||
|
||||
git clone https://github.com/SashLilac/cambridge
|
||||
|
||||
Alternatively, download the source code ZIP in the latest release.
|
||||
git clone https://github.com/joezeng/cambridge
|
||||
|
||||
Then, navigate to the root directory that you just cloned, and type:
|
||||
|
||||
@@ -47,19 +43,6 @@ Then, navigate to the root directory that you just cloned, and type:
|
||||
|
||||
It should run automatically!
|
||||
|
||||
## Windows
|
||||
|
||||
You do not need LÖVE on Windows, as it comes bundled with the program. Download the source code ZIP in the latest release, or if you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
|
||||
|
||||
Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
|
||||
|
||||
dist\windows\love.exe .
|
||||
|
||||
Alternatively, if you're on a 32-bit system, run this instead:
|
||||
|
||||
dist\win32\love.exe .
|
||||
|
||||
32-bit systems do not support rich presence integration.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -15,7 +15,9 @@ There are several classes of game modes. The modes that originate from other gam
|
||||
* The "G" series stand for "Guideline" games, or games that follow the Tetris Guideline.
|
||||
* GF - Tetris Friends (2007-2019)
|
||||
* GJ - Tetris Online Japan (2005-2011)
|
||||
* N stands for Nullpomino, only used for Phantom Mania N.
|
||||
* The "N" series stands for Nullpomino, only used for Phantom Mania N.
|
||||
* The "W" series stands for "Web" games, which are fanmade games released on the web.
|
||||
* WCB - RainComplex.net's Cat Boi Quatro.
|
||||
|
||||
MARATHON
|
||||
--------
|
||||
@@ -23,12 +25,15 @@ MARATHON
|
||||
Modes in which the goal is to play as well as possible over a limited game interval.
|
||||
|
||||
* **MARATHON 2020**: 2020 levels of pure pain. Can you make it all the way?
|
||||
* **MARATHON WCB**: CatBoiQuatro! Can you control the pieces?
|
||||
|
||||
From other games:
|
||||
* **MARATHON A1**: Tetris the Grand Master 1.
|
||||
* **MARATHON A2**: Tetris the Grand Master 2 (TAP Master).
|
||||
* **MARATHON A3**: Tetris the Grand Master 3 (no exams).
|
||||
* **MARATHON AX4**: Another mode from TGM Ace.
|
||||
* **MARATHON AX**: Normal mode from TGM Ace.
|
||||
* **MARATHON AX2**: Hi-Speed1 mode from TGM Ace.
|
||||
* **MARATHON AX3**: Hi-Speed2 mode from TGM Ace.
|
||||
* **MARATHON C89**: Nintendo NES Tetris. Can you transition and make it to the killscreen?
|
||||
|
||||
|
||||
@@ -43,6 +48,8 @@ From other games:
|
||||
* **SURVIVAL A1**: 20G mode from Tetris the Grand Master.
|
||||
* **SURVIVAL A2**: T.A. Death.
|
||||
* **SURVIVAL A3**: Ti Shirase.
|
||||
* **SURVIVAL AX**: Another mode from TGM Ace.
|
||||
* **SURVIVAL AX2**: Another2 mode from TGM Ace.
|
||||
|
||||
|
||||
RACE
|
||||
@@ -53,6 +60,14 @@ Modes with no levels, just a single timed goal.
|
||||
* **Race 40**: How fast can you clear 40 lines? No limits, no holds barred.
|
||||
|
||||
|
||||
CREDITS
|
||||
-------
|
||||
|
||||
Modes that are just the credit rolls of specific games.
|
||||
|
||||
* **CREDITS A3**: Tetris the Grand Master 3's famous M-roll.
|
||||
|
||||
|
||||
PHANTOM MANIA
|
||||
-------------
|
||||
|
||||
@@ -63,7 +78,6 @@ Modes where pieces turn invisible as soon as you lock them. One of Cambridge's s
|
||||
* **Phantom Mania 2**: Phantom Mania but way faster! Can you face a mode where even the garbage and the next preview turn invisible?
|
||||
|
||||
|
||||
|
||||
OTHER MODES
|
||||
-----------
|
||||
|
||||
|
||||
@@ -10,16 +10,13 @@ A ruleset consists of the following things:
|
||||
|
||||
If you're used to Nullpomino, you may notice a few things missing from that definition. For example, piece previews, hold queues, and randomizers have been moved to being game-specific rules, rather than rules that are changeable with the ruleset you use. Soft and hard drop behaviour is also game-specific now, so that times can be more plausibly compared across rulesets.
|
||||
|
||||
There are six rulesets currently supported:
|
||||
|
||||
Rotation system
|
||||
---------------
|
||||
A rotation system defines the following things:
|
||||
* The block offsets of each piece orientation.
|
||||
* The wall or floor kicks that will be attempted for each type of rotation.
|
||||
* Cambridge - a ruleset original to Cambridge, used for all custom modes. Supports 180-degree rotations!
|
||||
|
||||
There are four rotation systems currently supported:
|
||||
* SRS - the rotation system used in the Tetris Guideline games. Supports 180-degree rotations!
|
||||
* Ti-SRS - SRS but with no 180-degree rotations.
|
||||
|
||||
* Cambridge
|
||||
* Classic ARS
|
||||
* Ti-ARS
|
||||
* SRS
|
||||
* ARS - the rotation system from the original Tetris the Grand Master.
|
||||
* Ti-ARS - ARS with floorkicks! From TGM3: Terror Instinct.
|
||||
* Ace-ARS - ARS with floorkicks and move reset! From TGM ACE.
|
||||
32
funcs.lua
32
funcs.lua
@@ -1,5 +1,4 @@
|
||||
function copy(t)
|
||||
-- returns deep copy of t (as opposed to the shallow copy you get from var = t)
|
||||
if type(t) ~= "table" then return t end
|
||||
local meta = getmetatable(t)
|
||||
local target = {}
|
||||
@@ -8,8 +7,7 @@ function copy(t)
|
||||
return target
|
||||
end
|
||||
|
||||
function strTrueValues(tbl)
|
||||
-- returns a concatenation of all the keys in tbl with value true, separated with spaces
|
||||
function st(tbl)
|
||||
str = ""
|
||||
for k, v in pairs(tbl) do
|
||||
if v == true then
|
||||
@@ -19,16 +17,14 @@ function strTrueValues(tbl)
|
||||
return str
|
||||
end
|
||||
|
||||
function frameTime(min, sec, hth)
|
||||
-- returns a time in frames from a time in minutes-seconds-hundredths format
|
||||
if min == nil then min = 0 end
|
||||
if sec == nil then sec = 0 end
|
||||
if hth == nil then hth = 0 end
|
||||
return min*3600 + sec*60 + math.ceil(hth * 0.6)
|
||||
function sp(m, s, f)
|
||||
if m == nil then m = 0 end
|
||||
if s == nil then s = 0 end
|
||||
if f == nil then f = 0 end
|
||||
return m*3600 + s*60 + math.ceil(f * 0.6)
|
||||
end
|
||||
|
||||
function vAdd(v1, v2)
|
||||
-- returns the sum of vectors v1 and v2
|
||||
return {
|
||||
x = v1.x + v2.x,
|
||||
y = v1.y + v2.y
|
||||
@@ -36,7 +32,6 @@ function vAdd(v1, v2)
|
||||
end
|
||||
|
||||
function vNeg(v)
|
||||
-- returns the opposite of vector v
|
||||
return {
|
||||
x = -v.x,
|
||||
y = -v.y
|
||||
@@ -44,26 +39,17 @@ function vNeg(v)
|
||||
end
|
||||
|
||||
function formatTime(frames)
|
||||
-- returns a mm:ss:hh (h=hundredths) representation of the time in frames given
|
||||
if frames < 0 then return formatTime(0) end
|
||||
local min, sec, hund
|
||||
min = math.floor(frames/3600)
|
||||
sec = math.floor(frames/60) % 60
|
||||
hund = math.floor(frames/.6) % 100
|
||||
str = string.format("%02d:%02d.%02d", min, sec, hund)
|
||||
str = string.format("%02d", math.floor(frames / 3600)) .. ":"
|
||||
.. string.format("%02d", math.floor(frames / 60) % 60) .. "."
|
||||
.. string.format("%02d", math.floor(frames / 0.6) % 100)
|
||||
return str
|
||||
end
|
||||
|
||||
function formatBigNum(number)
|
||||
-- returns a string representing a number with commas as thousands separator (e.g. 12,345,678)
|
||||
local s = string.format("%d", number)
|
||||
local pos = string.len(s) % 3
|
||||
if pos == 0 then pos = 3 end
|
||||
return string.sub(s, 1, pos)
|
||||
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
|
||||
end
|
||||
|
||||
function Mod1(n, m)
|
||||
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
|
||||
return ((n-1) % m) + 1
|
||||
end
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,268 +0,0 @@
|
||||
local ffi = require "ffi"
|
||||
|
||||
|
||||
-- Get the host os to load correct lib
|
||||
local osname = love.system.getOS()
|
||||
local discordRPClib = nil
|
||||
|
||||
if osname == "Linux" then
|
||||
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.so")
|
||||
elseif osname == "OS X" then
|
||||
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.dylib")
|
||||
elseif osname == "Windows" then
|
||||
discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc.dll")
|
||||
else
|
||||
-- Else it crashes later on
|
||||
error(string.format("Discord rpc not supported on platform (%s)", osname))
|
||||
end
|
||||
|
||||
|
||||
ffi.cdef[[
|
||||
typedef struct DiscordRichPresence {
|
||||
const char* state; /* max 128 bytes */
|
||||
const char* details; /* max 128 bytes */
|
||||
int64_t startTimestamp;
|
||||
int64_t endTimestamp;
|
||||
const char* largeImageKey; /* max 32 bytes */
|
||||
const char* largeImageText; /* max 128 bytes */
|
||||
const char* smallImageKey; /* max 32 bytes */
|
||||
const char* smallImageText; /* max 128 bytes */
|
||||
const char* partyId; /* max 128 bytes */
|
||||
int partySize;
|
||||
int partyMax;
|
||||
const char* matchSecret; /* max 128 bytes */
|
||||
const char* joinSecret; /* max 128 bytes */
|
||||
const char* spectateSecret; /* max 128 bytes */
|
||||
int8_t instance;
|
||||
} DiscordRichPresence;
|
||||
|
||||
typedef struct DiscordUser {
|
||||
const char* userId;
|
||||
const char* username;
|
||||
const char* discriminator;
|
||||
const char* avatar;
|
||||
} DiscordUser;
|
||||
|
||||
typedef void (*readyPtr)(const DiscordUser* request);
|
||||
typedef void (*disconnectedPtr)(int errorCode, const char* message);
|
||||
typedef void (*erroredPtr)(int errorCode, const char* message);
|
||||
typedef void (*joinGamePtr)(const char* joinSecret);
|
||||
typedef void (*spectateGamePtr)(const char* spectateSecret);
|
||||
typedef void (*joinRequestPtr)(const DiscordUser* request);
|
||||
|
||||
typedef struct DiscordEventHandlers {
|
||||
readyPtr ready;
|
||||
disconnectedPtr disconnected;
|
||||
erroredPtr errored;
|
||||
joinGamePtr joinGame;
|
||||
spectateGamePtr spectateGame;
|
||||
joinRequestPtr joinRequest;
|
||||
} DiscordEventHandlers;
|
||||
|
||||
void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId);
|
||||
|
||||
void Discord_Shutdown(void);
|
||||
|
||||
void Discord_RunCallbacks(void);
|
||||
|
||||
void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
|
||||
void Discord_ClearPresence(void);
|
||||
|
||||
void Discord_Respond(const char* userid, int reply);
|
||||
|
||||
void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||
]]
|
||||
|
||||
local discordRPC = {} -- module table
|
||||
|
||||
-- proxy to detect garbage collection of the module
|
||||
discordRPC.gcDummy = newproxy(true)
|
||||
|
||||
local function unpackDiscordUser(request)
|
||||
return ffi.string(request.userId), ffi.string(request.username),
|
||||
ffi.string(request.discriminator), ffi.string(request.avatar)
|
||||
end
|
||||
|
||||
-- callback proxies
|
||||
-- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them
|
||||
-- luajit.org/ext_ffi_semantics.html
|
||||
local ready_proxy = ffi.cast("readyPtr", function(request)
|
||||
if discordRPC.ready then
|
||||
discordRPC.ready(unpackDiscordUser(request))
|
||||
end
|
||||
end)
|
||||
|
||||
local disconnected_proxy = ffi.cast("disconnectedPtr", function(errorCode, message)
|
||||
if discordRPC.disconnected then
|
||||
discordRPC.disconnected(errorCode, ffi.string(message))
|
||||
end
|
||||
end)
|
||||
|
||||
local errored_proxy = ffi.cast("erroredPtr", function(errorCode, message)
|
||||
if discordRPC.errored then
|
||||
discordRPC.errored(errorCode, ffi.string(message))
|
||||
end
|
||||
end)
|
||||
|
||||
local joinGame_proxy = ffi.cast("joinGamePtr", function(joinSecret)
|
||||
if discordRPC.joinGame then
|
||||
discordRPC.joinGame(ffi.string(joinSecret))
|
||||
end
|
||||
end)
|
||||
|
||||
local spectateGame_proxy = ffi.cast("spectateGamePtr", function(spectateSecret)
|
||||
if discordRPC.spectateGame then
|
||||
discordRPC.spectateGame(ffi.string(spectateSecret))
|
||||
end
|
||||
end)
|
||||
|
||||
local joinRequest_proxy = ffi.cast("joinRequestPtr", function(request)
|
||||
if discordRPC.joinRequest then
|
||||
discordRPC.joinRequest(unpackDiscordUser(request))
|
||||
end
|
||||
end)
|
||||
|
||||
-- helpers
|
||||
local function checkArg(arg, argType, argName, func, maybeNil)
|
||||
assert(type(arg) == argType or (maybeNil and arg == nil),
|
||||
string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"",
|
||||
argName, func, argType))
|
||||
end
|
||||
|
||||
local function checkStrArg(arg, maxLen, argName, func, maybeNil)
|
||||
if maxLen then
|
||||
assert(type(arg) == "string" and arg:len() <= maxLen or (maybeNil and arg == nil),
|
||||
string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d",
|
||||
argName, func, maxLen))
|
||||
else
|
||||
checkArg(arg, "string", argName, func, true)
|
||||
end
|
||||
end
|
||||
|
||||
local function checkIntArg(arg, maxBits, argName, func, maybeNil)
|
||||
maxBits = math.min(maxBits or 32, 52) -- lua number (double) can only store integers < 2^53
|
||||
local maxVal = 2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
|
||||
assert(type(arg) == "number" and math.floor(arg) == arg
|
||||
and arg < maxVal and arg >= -maxVal
|
||||
or (maybeNil and arg == nil),
|
||||
string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d",
|
||||
argName, func, maxVal))
|
||||
end
|
||||
|
||||
-- function wrappers
|
||||
function discordRPC.initialize(applicationId, autoRegister, optionalSteamId)
|
||||
local func = "discordRPC.Initialize"
|
||||
checkStrArg(applicationId, nil, "applicationId", func)
|
||||
checkArg(autoRegister, "boolean", "autoRegister", func)
|
||||
if optionalSteamId ~= nil then
|
||||
checkStrArg(optionalSteamId, nil, "optionalSteamId", func)
|
||||
end
|
||||
|
||||
local eventHandlers = ffi.new("struct DiscordEventHandlers")
|
||||
eventHandlers.ready = ready_proxy
|
||||
eventHandlers.disconnected = disconnected_proxy
|
||||
eventHandlers.errored = errored_proxy
|
||||
eventHandlers.joinGame = joinGame_proxy
|
||||
eventHandlers.spectateGame = spectateGame_proxy
|
||||
eventHandlers.joinRequest = joinRequest_proxy
|
||||
|
||||
discordRPClib.Discord_Initialize(applicationId, eventHandlers,
|
||||
autoRegister and 1 or 0, optionalSteamId)
|
||||
end
|
||||
|
||||
function discordRPC.shutdown()
|
||||
discordRPClib.Discord_Shutdown()
|
||||
end
|
||||
|
||||
function discordRPC.runCallbacks()
|
||||
discordRPClib.Discord_RunCallbacks()
|
||||
end
|
||||
-- http://luajit.org/ext_ffi_semantics.html#callback :
|
||||
-- It is not allowed, to let an FFI call into a C function (runCallbacks)
|
||||
-- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready).
|
||||
-- Usually this attempt is caught by the interpreter first and the C function
|
||||
-- is blacklisted for compilation.
|
||||
-- solution:
|
||||
-- "Then you'll need to manually turn off JIT-compilation with jit.off() for
|
||||
-- the surrounding Lua function that invokes such a message polling function."
|
||||
jit.off(discordRPC.runCallbacks)
|
||||
|
||||
function discordRPC.updatePresence(presence)
|
||||
local func = "discordRPC.updatePresence"
|
||||
checkArg(presence, "table", "presence", func)
|
||||
|
||||
-- -1 for string length because of 0-termination
|
||||
checkStrArg(presence.state, 127, "presence.state", func, true)
|
||||
checkStrArg(presence.details, 127, "presence.details", func, true)
|
||||
|
||||
checkIntArg(presence.startTimestamp, 64, "presence.startTimestamp", func, true)
|
||||
checkIntArg(presence.endTimestamp, 64, "presence.endTimestamp", func, true)
|
||||
|
||||
checkStrArg(presence.largeImageKey, 31, "presence.largeImageKey", func, true)
|
||||
checkStrArg(presence.largeImageText, 127, "presence.largeImageText", func, true)
|
||||
checkStrArg(presence.smallImageKey, 31, "presence.smallImageKey", func, true)
|
||||
checkStrArg(presence.smallImageText, 127, "presence.smallImageText", func, true)
|
||||
checkStrArg(presence.partyId, 127, "presence.partyId", func, true)
|
||||
|
||||
checkIntArg(presence.partySize, 32, "presence.partySize", func, true)
|
||||
checkIntArg(presence.partyMax, 32, "presence.partyMax", func, true)
|
||||
|
||||
checkStrArg(presence.matchSecret, 127, "presence.matchSecret", func, true)
|
||||
checkStrArg(presence.joinSecret, 127, "presence.joinSecret", func, true)
|
||||
checkStrArg(presence.spectateSecret, 127, "presence.spectateSecret", func, true)
|
||||
|
||||
checkIntArg(presence.instance, 8, "presence.instance", func, true)
|
||||
|
||||
local cpresence = ffi.new("struct DiscordRichPresence")
|
||||
cpresence.state = presence.state
|
||||
cpresence.details = presence.details
|
||||
cpresence.startTimestamp = presence.startTimestamp or 0
|
||||
cpresence.endTimestamp = presence.endTimestamp or 0
|
||||
cpresence.largeImageKey = presence.largeImageKey
|
||||
cpresence.largeImageText = presence.largeImageText
|
||||
cpresence.smallImageKey = presence.smallImageKey
|
||||
cpresence.smallImageText = presence.smallImageText
|
||||
cpresence.partyId = presence.partyId
|
||||
cpresence.partySize = presence.partySize or 0
|
||||
cpresence.partyMax = presence.partyMax or 0
|
||||
cpresence.matchSecret = presence.matchSecret
|
||||
cpresence.joinSecret = presence.joinSecret
|
||||
cpresence.spectateSecret = presence.spectateSecret
|
||||
cpresence.instance = presence.instance or 0
|
||||
|
||||
discordRPClib.Discord_UpdatePresence(cpresence)
|
||||
end
|
||||
|
||||
function discordRPC.clearPresence()
|
||||
discordRPClib.Discord_ClearPresence()
|
||||
end
|
||||
|
||||
local replyMap = {
|
||||
no = 0,
|
||||
yes = 1,
|
||||
ignore = 2
|
||||
}
|
||||
|
||||
-- maybe let reply take ints too (0, 1, 2) and add constants to the module
|
||||
function discordRPC.respond(userId, reply)
|
||||
checkStrArg(userId, nil, "userId", "discordRPC.respond")
|
||||
assert(replyMap[reply], "Argument 'reply' to discordRPC.respond has to be one of \"yes\", \"no\" or \"ignore\"")
|
||||
discordRPClib.Discord_Respond(userId, replyMap[reply])
|
||||
end
|
||||
|
||||
-- garbage collection callback
|
||||
getmetatable(discordRPC.gcDummy).__gc = function()
|
||||
discordRPC.shutdown()
|
||||
ready_proxy:free()
|
||||
disconnected_proxy:free()
|
||||
errored_proxy:free()
|
||||
joinGame_proxy:free()
|
||||
spectateGame_proxy:free()
|
||||
joinRequest_proxy:free()
|
||||
end
|
||||
|
||||
return discordRPC
|
||||
@@ -6,7 +6,7 @@ bgm = {
|
||||
}
|
||||
|
||||
local current_bgm = nil
|
||||
local bgm_locked = true
|
||||
local bgm_locked = false
|
||||
|
||||
function switchBGM(sound, subsound)
|
||||
if bgm_locked then return end
|
||||
|
||||
@@ -20,54 +20,35 @@ backgrounds = {
|
||||
love.graphics.newImage("res/backgrounds/1800-railways.png"),
|
||||
love.graphics.newImage("res/backgrounds/1900-world-wide-web.png"),
|
||||
title = love.graphics.newImage("res/backgrounds/title_v0.1.png"),
|
||||
input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"),
|
||||
game_config = love.graphics.newImage("res/backgrounds/options-gears.png"),
|
||||
}
|
||||
|
||||
blocks = {
|
||||
["2tie"] = {
|
||||
R = love.graphics.newImage("res/img/s1.png"),
|
||||
O = love.graphics.newImage("res/img/s3.png"),
|
||||
Y = love.graphics.newImage("res/img/s7.png"),
|
||||
G = love.graphics.newImage("res/img/s6.png"),
|
||||
C = love.graphics.newImage("res/img/s2.png"),
|
||||
B = love.graphics.newImage("res/img/s4.png"),
|
||||
M = love.graphics.newImage("res/img/s5.png"),
|
||||
I = love.graphics.newImage("res/img/s1.png"),
|
||||
J = love.graphics.newImage("res/img/s4.png"),
|
||||
L = love.graphics.newImage("res/img/s3.png"),
|
||||
O = love.graphics.newImage("res/img/s7.png"),
|
||||
S = love.graphics.newImage("res/img/s5.png"),
|
||||
T = love.graphics.newImage("res/img/s2.png"),
|
||||
Z = love.graphics.newImage("res/img/s6.png"),
|
||||
F = love.graphics.newImage("res/img/s9.png"),
|
||||
G = love.graphics.newImage("res/img/s9.png"),
|
||||
X = love.graphics.newImage("res/img/s9.png"),
|
||||
},
|
||||
["bone"] = {
|
||||
R = love.graphics.newImage("res/img/bone.png"),
|
||||
I = love.graphics.newImage("res/img/bone.png"),
|
||||
J = love.graphics.newImage("res/img/bone.png"),
|
||||
L = love.graphics.newImage("res/img/bone.png"),
|
||||
O = love.graphics.newImage("res/img/bone.png"),
|
||||
Y = love.graphics.newImage("res/img/bone.png"),
|
||||
S = love.graphics.newImage("res/img/bone.png"),
|
||||
T = love.graphics.newImage("res/img/bone.png"),
|
||||
Z = love.graphics.newImage("res/img/bone.png"),
|
||||
F = love.graphics.newImage("res/img/bone.png"),
|
||||
G = love.graphics.newImage("res/img/bone.png"),
|
||||
C = love.graphics.newImage("res/img/bone.png"),
|
||||
B = love.graphics.newImage("res/img/bone.png"),
|
||||
M = love.graphics.newImage("res/img/bone.png"),
|
||||
X = love.graphics.newImage("res/img/bone.png"),
|
||||
}
|
||||
}
|
||||
|
||||
ColourSchemes = {
|
||||
Arika = {
|
||||
I = "R",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "M",
|
||||
Z = "G",
|
||||
O = "Y",
|
||||
T = "C",
|
||||
},
|
||||
TTC = {
|
||||
I = "C",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "G",
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
},
|
||||
}
|
||||
|
||||
for name, blockset in pairs(blocks) do
|
||||
for shape, image in pairs(blockset) do
|
||||
image:setFilter("nearest")
|
||||
@@ -80,4 +61,4 @@ misc_graphics = {
|
||||
go = love.graphics.newImage("res/img/go.png"),
|
||||
select_mode = love.graphics.newImage("res/img/select_mode.png"),
|
||||
strike = love.graphics.newImage("res/img/strike.png"),
|
||||
}
|
||||
}
|
||||
|
||||
58
load/rpc.lua
58
load/rpc.lua
@@ -1,58 +0,0 @@
|
||||
print("Loading discord RPC...")
|
||||
DiscordRPC = {
|
||||
loaded = false
|
||||
}
|
||||
local success, RPC = pcall(require, "libs.discordRPC")
|
||||
if success then
|
||||
DiscordRPC.loaded = true
|
||||
DiscordRPC.appId = "599778517789573120"
|
||||
|
||||
function RPC.ready(userId, username, discriminator, avatar)
|
||||
print(string.format("Discord: ready (%s, %s, %s, %s)", userId, username, discriminator, avatar))
|
||||
end
|
||||
|
||||
function RPC.disconnected(errorCode, message)
|
||||
print(string.format("Discord: disconnected (%d: %s)", errorCode, message))
|
||||
end
|
||||
|
||||
function RPC.errored(errorCode, message)
|
||||
print(string.format("Discord: error (%d: %s)", errorCode, message))
|
||||
end
|
||||
|
||||
function RPC.joinGame(joinSecret)
|
||||
print(string.format("Discord: join (%s)", joinSecret))
|
||||
end
|
||||
|
||||
function RPC.spectateGame(spectateSecret)
|
||||
print(string.format("Discord: spectate (%s)", spectateSecret))
|
||||
end
|
||||
|
||||
function RPC.joinRequest(userId, username, discriminator, avatar)
|
||||
print(string.format("Discord: join request (%s, %s, %s, %s)", userId, username, discriminator, avatar))
|
||||
RPC.respond(userId, "yes")
|
||||
end
|
||||
|
||||
RPC.initialize(DiscordRPC.appId, true)
|
||||
local now = os.time(os.date("*t"))
|
||||
|
||||
DiscordRPC.RPC = RPC
|
||||
print("DiscordRPC successfully loaded.")
|
||||
else
|
||||
print("DiscordRPC failed to load!")
|
||||
print(RPC)
|
||||
end
|
||||
|
||||
DiscordRPC.presence = {
|
||||
startTimestamp = now,
|
||||
details = "Loading game...",
|
||||
state = "",
|
||||
largeImageKey = "icon2",
|
||||
largeImageText = "Arcade Stacker",
|
||||
smallImageKey = "",
|
||||
smallImageText = ""
|
||||
}
|
||||
|
||||
function DiscordRPC:update(newstuff)
|
||||
for k, v in pairs(newstuff) do self.presence[k] = v end
|
||||
if self.loaded then self.RPC.updatePresence(self.presence) end
|
||||
end
|
||||
@@ -10,10 +10,6 @@ sounds = {
|
||||
},
|
||||
move = love.audio.newSource("res/se/move.wav", "static"),
|
||||
bottom = love.audio.newSource("res/se/bottom.wav", "static"),
|
||||
cursor = love.audio.newSource("res/se/cursor.wav", "static"),
|
||||
cursor_lr = love.audio.newSource("res/se/cursor_lr.wav", "static"),
|
||||
main_decide = love.audio.newSource("res/se/main_decide.wav", "static"),
|
||||
mode_decide = love.audio.newSource("res/se/mode_decide.wav", "static"),
|
||||
}
|
||||
|
||||
function playSE(sound, subsound)
|
||||
|
||||
33
main.lua
33
main.lua
@@ -1,7 +1,6 @@
|
||||
function love.load()
|
||||
math.randomseed(os.time())
|
||||
highscores = {}
|
||||
require "load.rpc"
|
||||
require "load.graphics"
|
||||
require "load.fonts"
|
||||
require "load.sounds"
|
||||
@@ -12,16 +11,10 @@ function love.load()
|
||||
config["side_next"] = false
|
||||
config["reverse_rotate"] = true
|
||||
config["fullscreen"] = false
|
||||
config["das_last_key"] = false
|
||||
|
||||
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
||||
|
||||
if not config.gamesettings then config.gamesettings = {} end
|
||||
for _, option in ipairs(GameConfigScene.options) do
|
||||
if not config.gamesettings[option[1]] then
|
||||
config.gamesettings[option[1]] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if not config.input then
|
||||
config.input = {}
|
||||
scene = InputConfigScene()
|
||||
@@ -30,28 +23,6 @@ function love.load()
|
||||
if config.current_ruleset then current_ruleset = config.current_ruleset end
|
||||
scene = TitleScene()
|
||||
end
|
||||
|
||||
game_modes = {}
|
||||
mode_list = love.filesystem.getDirectoryItems("tetris/modes")
|
||||
for i=1,#mode_list do
|
||||
if(mode_list[i] ~= "gamemode.lua" and mode_list[i] ~= "unrefactored_modes") then
|
||||
game_modes[#game_modes+1] = require ("tetris.modes."..string.sub(mode_list[i],1,-5))
|
||||
end
|
||||
end
|
||||
rulesets = {}
|
||||
rule_list = love.filesystem.getDirectoryItems("tetris/rulesets")
|
||||
for i=1,#rule_list do
|
||||
if(rule_list[i] ~= "ruleset.lua" and rule_list[i] ~= "unrefactored_rulesets") then
|
||||
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
|
||||
end
|
||||
end
|
||||
--sort mode/rule lists
|
||||
local function padnum(d) return ("%03d%s"):format(#d, d) end
|
||||
table.sort(game_modes, function(a,b)
|
||||
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||
table.sort(rulesets, function(a,b)
|
||||
return tostring(a.name):gsub("%d+",padnum) < tostring(b.name):gsub("%d+",padnum) end)
|
||||
|
||||
end
|
||||
|
||||
local TARGET_FPS = 60
|
||||
@@ -94,7 +65,7 @@ end
|
||||
|
||||
function love.draw()
|
||||
love.graphics.push()
|
||||
|
||||
|
||||
-- get offset matrix
|
||||
love.graphics.setDefaultFilter("linear", "nearest")
|
||||
local width = love.graphics.getWidth()
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 229 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,9 +7,8 @@ function Scene:update() end
|
||||
function Scene:render() end
|
||||
function Scene:onKeyPress() end
|
||||
|
||||
ExitScene = require "scene.exit"
|
||||
GameScene = require "scene.game"
|
||||
ModeSelectScene = require "scene.mode_select"
|
||||
InputConfigScene = require "scene.input_config"
|
||||
GameConfigScene = require "scene.game_config"
|
||||
ConfigScene = require "scene.config"
|
||||
TitleScene = require "scene.title"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
local ExitScene = Scene:extend()
|
||||
require 'load.save'
|
||||
|
||||
ExitScene.title = "Exit Game"
|
||||
|
||||
function ExitScene:new()
|
||||
end
|
||||
|
||||
function ExitScene:update()
|
||||
love.event.quit()
|
||||
end
|
||||
|
||||
function ExitScene:render()
|
||||
end
|
||||
|
||||
function ExitScene:changeOption(rel)
|
||||
end
|
||||
|
||||
function ExitScene:onKeyPress(e)
|
||||
end
|
||||
|
||||
return ExitScene
|
||||
|
||||
@@ -5,10 +5,6 @@ function GameScene:new(game_mode, ruleset)
|
||||
self.game = game_mode()
|
||||
self.ruleset = ruleset()
|
||||
self.game:initialize(self.ruleset)
|
||||
DiscordRPC:update({
|
||||
details = self.game.rpc_details,
|
||||
state = self.game.name,
|
||||
})
|
||||
end
|
||||
|
||||
function GameScene:update()
|
||||
@@ -61,21 +57,11 @@ end
|
||||
|
||||
function GameScene:onKeyPress(e)
|
||||
if (self.game.completed) and
|
||||
(e.scancode == "return" or e.scancode == "escape") and e.isRepeat == false then
|
||||
e.scancode == "return" and e.isRepeat == false then
|
||||
highscore_entry = self.game:getHighscoreData()
|
||||
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
|
||||
submitHighscore(highscore_hash, highscore_entry)
|
||||
scene = ModeSelectScene()
|
||||
elseif (e.scancode == config.input.retry) then
|
||||
-- fuck this, this is hacky but the way this codebase is setup prevents anything else
|
||||
-- it seems like all the values that get touched in the child gamemode class
|
||||
-- stop being linked to the values of the GameMode superclass because of how `mt.__index` works
|
||||
-- not even sure this is the actual problem, but I don't want to have to rebuild everything about
|
||||
-- the core organisation of everything. this hacky way will have to do until someone figures out something.
|
||||
love.keypressed("escape", "escape", false)
|
||||
love.keypressed("return", "return", false)
|
||||
elseif e.scancode == "escape" then
|
||||
scene = ModeSelectScene()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
local ConfigScene = Scene:extend()
|
||||
|
||||
ConfigScene.title = "Game Settings"
|
||||
|
||||
require 'load.save'
|
||||
|
||||
ConfigScene.options = {
|
||||
-- this serves as reference to what the options' values mean i guess?
|
||||
{"manlock", "Manual locking", {"Per ruleset","Per gamemode","Harddrop", "Softdrop"}},
|
||||
{"piece_colour", "Piece Colours", {"Per ruleset", "Arika", "TTC"}},
|
||||
{"world_reverse", "World Reverse", {"No", "Yes"}},
|
||||
}
|
||||
local optioncount = #ConfigScene.options
|
||||
|
||||
function ConfigScene:new()
|
||||
-- load current config
|
||||
self.config = config.input
|
||||
self.highlight = 1
|
||||
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing game settings",
|
||||
})
|
||||
end
|
||||
|
||||
function ConfigScene:update()
|
||||
end
|
||||
|
||||
function ConfigScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds["game_config"],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.print("GAME SETTINGS", 80, 40)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
love.graphics.rectangle("fill", 20, 98 + self.highlight * 20, 170, 22)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for i, option in ipairs(ConfigScene.options) do
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(option[2], 40, 100 + i * 20, 150, "left")
|
||||
for j, setting in ipairs(option[3]) do
|
||||
love.graphics.setColor(1, 1, 1, config.gamesettings[option[1]] == j and 1 or 0.5)
|
||||
love.graphics.printf(setting, 100 + 110 * j, 100 + i * 20, 100, "center")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ConfigScene:onKeyPress(e)
|
||||
if e.scancode == "return" and e.isRepeat == false then
|
||||
playSE("mode_decide")
|
||||
saveConfig()
|
||||
scene = TitleScene()
|
||||
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
|
||||
playSE("cursor")
|
||||
self.highlight = Mod1(self.highlight-1, optioncount)
|
||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
||||
playSE("cursor")
|
||||
self.highlight = Mod1(self.highlight+1, optioncount)
|
||||
elseif (e.scancode == config.input["left"] or e.scancode == "left") and e.isRepeat == false then
|
||||
playSE("cursor_lr")
|
||||
local option = ConfigScene.options[self.highlight]
|
||||
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[3])
|
||||
elseif (e.scancode == config.input["right"] or e.scancode == "right") and e.isRepeat == false then
|
||||
playSE("cursor_lr")
|
||||
local option = ConfigScene.options[self.highlight]
|
||||
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[3])
|
||||
elseif e.scancode == "escape" then
|
||||
loadSave()
|
||||
scene = TitleScene()
|
||||
end
|
||||
end
|
||||
|
||||
return ConfigScene
|
||||
@@ -15,35 +15,22 @@ local configurable_inputs = {
|
||||
"rotate_right2",
|
||||
"rotate_180",
|
||||
"hold",
|
||||
"retry",
|
||||
}
|
||||
|
||||
function ConfigScene:new()
|
||||
-- load current config
|
||||
self.config = config.input
|
||||
self.input_state = 1
|
||||
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Changing input config",
|
||||
})
|
||||
end
|
||||
|
||||
function ConfigScene:update()
|
||||
end
|
||||
|
||||
function ConfigScene:render()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(
|
||||
backgrounds["input_config"],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for i, input in pairs(configurable_inputs) do
|
||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||
if config.input[input] then
|
||||
love.graphics.printf(input, 40, 50 + i * 20, 200, "left")
|
||||
love.graphics.printf(
|
||||
love.keyboard.getKeyFromScancode(config.input[input]) .. " (" .. config.input[input] .. ")",
|
||||
240, 50 + i * 20, 200, "left"
|
||||
@@ -67,13 +54,8 @@ function ConfigScene:onKeyPress(e)
|
||||
self.input_state = 1
|
||||
end
|
||||
else
|
||||
if e.scancode == "escape" then
|
||||
loadSave()
|
||||
scene = TitleScene()
|
||||
else
|
||||
config.input[configurable_inputs[self.input_state]] = e.scancode
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
config.input[configurable_inputs[self.input_state]] = e.scancode
|
||||
self.input_state = self.input_state + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
123
scene/mode_select.lua
Executable file → Normal file
123
scene/mode_select.lua
Executable file → Normal file
@@ -5,57 +5,110 @@ ModeSelectScene.title = "Game Start"
|
||||
current_mode = 1
|
||||
current_ruleset = 1
|
||||
|
||||
MAX_MODES = 19
|
||||
|
||||
game_modes = {
|
||||
require 'tetris.modes.marathon_2020',
|
||||
require 'tetris.modes.survival_2020',
|
||||
require 'tetris.modes.strategy',
|
||||
require 'tetris.modes.interval_training',
|
||||
require 'tetris.modes.pacer_test',
|
||||
require 'tetris.modes.marathon_wcb',
|
||||
require 'tetris.modes.demon_mode',
|
||||
require 'tetris.modes.phantom_mania',
|
||||
require 'tetris.modes.phantom_mania2',
|
||||
require 'tetris.modes.phantom_mania_n',
|
||||
require 'tetris.modes.race_40',
|
||||
require 'tetris.modes.marathon_a1',
|
||||
require 'tetris.modes.marathon_a2',
|
||||
require 'tetris.modes.marathon_a3',
|
||||
require 'tetris.modes.marathon_ax',
|
||||
require 'tetris.modes.marathon_ax2',
|
||||
require 'tetris.modes.marathon_ax3',
|
||||
require 'tetris.modes.marathon_c89',
|
||||
require 'tetris.modes.survival_a1',
|
||||
require 'tetris.modes.survival_a2',
|
||||
require 'tetris.modes.survival_a3',
|
||||
require 'tetris.modes.survival_ax',
|
||||
require 'tetris.modes.survival_ax2',
|
||||
require 'tetris.modes.credits_a3',
|
||||
}
|
||||
|
||||
rulesets = {
|
||||
require 'tetris.rulesets.cambridge',
|
||||
require 'tetris.rulesets.standard',
|
||||
require 'tetris.rulesets.standard_ti',
|
||||
require 'tetris.rulesets.arika',
|
||||
require 'tetris.rulesets.arika_ti',
|
||||
require 'tetris.rulesets.arika_ace',
|
||||
--require 'tetris.rulesets.bonkers',
|
||||
--require 'tetris.rulesets.shirase',
|
||||
--require 'tetris.rulesets.super302',
|
||||
}
|
||||
|
||||
function ModeSelectScene:new()
|
||||
self.menu_state = {
|
||||
mode = current_mode,
|
||||
ruleset = current_ruleset,
|
||||
select = "mode",
|
||||
}
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = "Choosing a mode",
|
||||
})
|
||||
end
|
||||
|
||||
function ModeSelectScene:update()
|
||||
end
|
||||
|
||||
function getCursorHeight(x)
|
||||
return 78 + 20 * x
|
||||
end
|
||||
|
||||
|
||||
function ModeSelectScene:drawList(name, items, cursor, x)
|
||||
local start, finish
|
||||
if table.getn(items) < MAX_MODES then
|
||||
start = 1
|
||||
finish = table.getn(items)
|
||||
else
|
||||
if cursor < 10 then
|
||||
start = 1
|
||||
finish = 19
|
||||
elseif cursor > table.getn(items) - 9 then
|
||||
start = table.getn(items) - 18
|
||||
finish = table.getn(items)
|
||||
else
|
||||
start = cursor - 9
|
||||
finish = cursor + 9
|
||||
end
|
||||
end
|
||||
|
||||
if self.menu_state.select == name then
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
else
|
||||
love.graphics.setColor(1, 1, 1, 0.25)
|
||||
end
|
||||
love.graphics.rectangle("fill", x, getCursorHeight(cursor - start), 240, 22)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
for idx = start, finish do
|
||||
local item = items[idx]
|
||||
love.graphics.printf(item.name, x + 20, 80 + 20 * (idx - start), 200, "left")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ModeSelectScene:render()
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
|
||||
love.graphics.draw(
|
||||
backgrounds[0],
|
||||
0, 0, 0,
|
||||
0.5, 0.5
|
||||
)
|
||||
|
||||
if self.menu_state.select == "mode" then
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
love.graphics.setColor(1, 1, 1, 0.25)
|
||||
end
|
||||
love.graphics.rectangle("fill", 20, 258, 240, 22)
|
||||
love.graphics.draw(misc_graphics["select_mode"], 20, 30)
|
||||
|
||||
if self.menu_state.select == "mode" then
|
||||
love.graphics.setColor(1, 1, 1, 0.25)
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
end
|
||||
love.graphics.rectangle("fill", 340, 258, 200, 22)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for idx, mode in pairs(game_modes) do
|
||||
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
|
||||
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
|
||||
end
|
||||
end
|
||||
for idx, ruleset in pairs(rulesets) do
|
||||
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
|
||||
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
|
||||
end
|
||||
end
|
||||
self:drawList("mode", game_modes, self.menu_state.mode, 20)
|
||||
self:drawList("ruleset", rulesets, self.menu_state.ruleset, 320)
|
||||
end
|
||||
|
||||
function ModeSelectScene:onKeyPress(e)
|
||||
@@ -64,21 +117,15 @@ function ModeSelectScene:onKeyPress(e)
|
||||
current_ruleset = self.menu_state.ruleset
|
||||
config.current_mode = current_mode
|
||||
config.current_ruleset = current_ruleset
|
||||
playSE("mode_decide")
|
||||
saveConfig()
|
||||
scene = GameScene(game_modes[self.menu_state.mode], rulesets[self.menu_state.ruleset])
|
||||
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
|
||||
self:changeOption(-1)
|
||||
playSE("cursor")
|
||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
||||
self:changeOption(1)
|
||||
playSE("cursor")
|
||||
elseif (e.scancode == config.input["left"] or e.scancode == "left") or
|
||||
(e.scancode == config.input["right"] or e.scancode == "right") then
|
||||
self:switchSelect()
|
||||
playSE("cursor_lr")
|
||||
elseif e.scancode == "escape" then
|
||||
scene = TitleScene()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,31 +3,10 @@ local TitleScene = Scene:extend()
|
||||
local main_menu_screens = {
|
||||
ModeSelectScene,
|
||||
InputConfigScene,
|
||||
GameConfigScene,
|
||||
ExitScene,
|
||||
}
|
||||
|
||||
local mainmenuidle = {
|
||||
"Idle",
|
||||
"On title screen",
|
||||
"On main menu screen",
|
||||
"Twiddling their thumbs",
|
||||
"Admiring the main menu's BG",
|
||||
"Waiting for spring to come",
|
||||
"Actually not playing",
|
||||
"Contemplating collecting stars",
|
||||
"Preparing to put the block!!",
|
||||
"Having a nap",
|
||||
"In menus",
|
||||
"Bottom text",
|
||||
}
|
||||
|
||||
function TitleScene:new()
|
||||
self.main_menu_state = 1
|
||||
DiscordRPC:update({
|
||||
details = "In menus",
|
||||
state = mainmenuidle[math.random(#mainmenuidle)],
|
||||
})
|
||||
end
|
||||
|
||||
function TitleScene:update()
|
||||
@@ -59,16 +38,11 @@ end
|
||||
|
||||
function TitleScene:onKeyPress(e)
|
||||
if e.scancode == "return" and e.isRepeat == false then
|
||||
playSE("main_decide")
|
||||
scene = main_menu_screens[self.main_menu_state]()
|
||||
elseif (e.scancode == config.input["up"] or e.scancode == "up") and e.isRepeat == false then
|
||||
self:changeOption(-1)
|
||||
playSE("cursor")
|
||||
elseif (e.scancode == config.input["down"] or e.scancode == "down") and e.isRepeat == false then
|
||||
self:changeOption(1)
|
||||
playSE("cursor")
|
||||
elseif e.scancode == "escape" and e.isRepeat == false then
|
||||
love.event.quit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ local Object = require 'libs.classic'
|
||||
local Grid = Object:extend()
|
||||
|
||||
local empty = { skin = "", colour = "" }
|
||||
local oob = { skin = "", colour = "" }
|
||||
local block = { skin = "2tie", colour = "X" }
|
||||
|
||||
function Grid:new()
|
||||
self.grid = {}
|
||||
@@ -28,15 +26,8 @@ function Grid:clear()
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:getCell(x, y)
|
||||
if x < 1 or x > 10 or y > 24 then return oob
|
||||
elseif y < 1 then return empty
|
||||
else return self.grid[y][x]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:isOccupied(x, y)
|
||||
return self:getCell(x+1, y+1) ~= empty
|
||||
return self.grid[y+1][x+1] ~= empty
|
||||
end
|
||||
|
||||
function Grid:isRowFull(row)
|
||||
@@ -55,7 +46,7 @@ function Grid:canPlacePiece(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if self:isOccupied(x, y) then
|
||||
if x >= 10 or x < 0 or y >= 24 or y < 0 or self.grid[y+1][x+1] ~= empty then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -67,12 +58,12 @@ function Grid:canPlaceBigPiece(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if (
|
||||
self:isOccupied(x * 2 + 0, y * 2 + 0)
|
||||
or self:isOccupied(x * 2 + 1, y * 2 + 0)
|
||||
or self:isOccupied(x * 2 + 0, y * 2 + 1)
|
||||
or self:isOccupied(x * 2 + 1, y * 2 + 1)
|
||||
) then
|
||||
if x >= 5 or x < 0 or y >= 12 or y < 0 or
|
||||
self.grid[y * 2 + 1][x * 2 + 1] ~= empty or
|
||||
self.grid[y * 2 + 1][x * 2 + 2] ~= empty or
|
||||
self.grid[y * 2 + 2][x * 2 + 1] ~= empty or
|
||||
self.grid[y * 2 + 2][x * 2 + 2] ~= empty
|
||||
then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -89,7 +80,7 @@ function Grid:canPlacePieceInVisibleGrid(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if y < 4 or self:isOccupied(x, y) ~= empty then
|
||||
if x >= 10 or x < 0 or y >= 24 or y < 4 or self.grid[y+1][x+1] ~= empty then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -142,34 +133,14 @@ function Grid:copyBottomRow()
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
|
||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or {
|
||||
skin = self.grid[23][col].skin,
|
||||
colour = "G"
|
||||
}
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:garbageRise(row_vals)
|
||||
for row = 1, 23 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (row_vals[col] == "e") and empty or block
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyFourWide()
|
||||
for row = 1, 24 do
|
||||
local x = self.grid[row]
|
||||
x[1] = x[1]~=block and block or x[1]
|
||||
x[2] = x[2]~=block and block or x[2]
|
||||
x[3] = x[3]~=block and block or x[3]
|
||||
x[8] = x[8]~=block and block or x[8]
|
||||
x[9] = x[9]~=block and block or x[9]
|
||||
x[10] = x[10]~=block and block or x[10]
|
||||
end
|
||||
end
|
||||
function Grid:applyPiece(piece)
|
||||
if piece.big then
|
||||
self:applyBigPiece(piece)
|
||||
@@ -179,12 +150,10 @@ function Grid:applyPiece(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
x = piece.position.x + offset.x
|
||||
y = piece.position.y + offset.y
|
||||
if y + 1 > 0 then
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
}
|
||||
end
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.shape
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -195,60 +164,15 @@ function Grid:applyBigPiece(piece)
|
||||
y = piece.position.y + offset.y
|
||||
for a = 1, 2 do
|
||||
for b = 1, 2 do
|
||||
if y*2+a > 0 then
|
||||
self.grid[y*2+a][x*2+b] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
}
|
||||
end
|
||||
self.grid[y*2+a][x*2+b] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.shape
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:checkForBravo(cleared_row_count)
|
||||
for i = 0, 23 - cleared_row_count do
|
||||
for j = 0, 9 do
|
||||
if self:isOccupied(j, i) then return false end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:checkSecretGrade()
|
||||
local sgrade = 0
|
||||
for i=23,5,-1 do
|
||||
local validLine = true
|
||||
local emptyCell = 0
|
||||
if i > 13 then
|
||||
emptyCell = 23-i
|
||||
end
|
||||
if i <= 13 then
|
||||
emptyCell = i-5
|
||||
end
|
||||
for j=0,9 do
|
||||
if (not self:isOccupied(j,i) and j ~= emptyCell) or (j == emptyCell and self:isOccupied(j,i)) then
|
||||
validLine = false
|
||||
end
|
||||
end
|
||||
if not self:isOccupied(emptyCell,i-1) then
|
||||
validLine = false
|
||||
end
|
||||
if(validLine) then
|
||||
sgrade = sgrade + 1
|
||||
else
|
||||
return sgrade
|
||||
end
|
||||
end
|
||||
--[[
|
||||
if(sgrade == 0) then return ""
|
||||
elseif(sgrade < 10) then return 10-sgrade
|
||||
elseif(sgrade < 19) then return "S"..(sgrade-9) end
|
||||
return "GM"
|
||||
--]]
|
||||
return sgrade
|
||||
end
|
||||
|
||||
function Grid:update()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
@@ -260,7 +184,7 @@ function Grid:update()
|
||||
end
|
||||
|
||||
function Grid:draw()
|
||||
for y = 5, 24 do
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid_age[y][x] < 1 then
|
||||
@@ -292,7 +216,7 @@ function Grid:draw()
|
||||
end
|
||||
|
||||
function Grid:drawInvisible(opacity_function, garbage_opacity_function)
|
||||
for y = 5, 24 do
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid[y][x].colour == "X" then
|
||||
|
||||
@@ -2,7 +2,7 @@ local Object = require 'libs.classic'
|
||||
|
||||
local Piece = Object:extend()
|
||||
|
||||
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
|
||||
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, big)
|
||||
self.shape = shape
|
||||
self.rotation = rotation
|
||||
self.position = position
|
||||
@@ -10,7 +10,6 @@ function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay
|
||||
self.gravity = gravity
|
||||
self.lock_delay = lock_delay
|
||||
self.skin = skin
|
||||
self.colour = colour
|
||||
self.ghost = false
|
||||
self.locked = false
|
||||
self.big = big
|
||||
@@ -22,7 +21,7 @@ function Piece:withOffset(offset)
|
||||
return Piece(
|
||||
self.shape, self.rotation,
|
||||
{x = self.position.x + offset.x, y = self.position.y + offset.y},
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big
|
||||
)
|
||||
end
|
||||
|
||||
@@ -32,7 +31,7 @@ function Piece:withRelativeRotation(rot)
|
||||
while new_rot >= 4 do new_rot = new_rot - 4 end
|
||||
return Piece(
|
||||
self.shape, new_rot, self.position,
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.big
|
||||
)
|
||||
end
|
||||
|
||||
@@ -78,12 +77,15 @@ function Piece:setRelativeRotation(rot)
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:moveInGrid(step, squares, grid)
|
||||
function Piece:moveInGrid(step, squares, grid, instant)
|
||||
local moved = false
|
||||
for x = 1, squares do
|
||||
if grid:canPlacePiece(self:withOffset(step)) then
|
||||
moved = true
|
||||
self:setOffset(step)
|
||||
if instant then
|
||||
self:dropToBottom(grid)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
@@ -98,7 +100,7 @@ end
|
||||
|
||||
function Piece:dropToBottom(grid)
|
||||
local piece_y = self.position.y
|
||||
self:dropSquares(math.huge, grid)
|
||||
self:dropSquares(20, grid)
|
||||
self.gravity = 0
|
||||
if self.position.y > piece_y then
|
||||
-- if it got dropped any, also reset lock delay
|
||||
@@ -149,13 +151,13 @@ function Piece:draw(opacity, brightness, grid, partial_das)
|
||||
local y = self.position.y + offset.y
|
||||
if self.big then
|
||||
love.graphics.draw(
|
||||
blocks[self.skin][self.colour],
|
||||
blocks[self.skin][self.shape],
|
||||
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
|
||||
0, 2, 2
|
||||
)
|
||||
else
|
||||
love.graphics.draw(
|
||||
blocks[self.skin][self.colour],
|
||||
blocks[self.skin][self.shape],
|
||||
64+x*16+partial_das, 16+y*16+gravity_offset
|
||||
)
|
||||
end
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local SurvivalA3Game = require 'tetris.modes.survival_a3'
|
||||
|
||||
local FourWideGame = SurvivalA3Game:extend()
|
||||
|
||||
FourWideGame.name = "4-wide Simulator"
|
||||
FourWideGame.hash = "4wide"
|
||||
FourWideGame.tagline = "The board has gotten narrower! Can you survive the increasing speeds?"
|
||||
|
||||
function FourWideGame:initialize(ruleset)
|
||||
self.super:initialize(ruleset)
|
||||
self.grid:applyFourWide()
|
||||
end
|
||||
|
||||
local cleared_row_levels = {1, 2, 4, 6}
|
||||
|
||||
function FourWideGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = self.level + cleared_row_levels[cleared_row_count]
|
||||
self:updateSectionTimes(self.level, new_level)
|
||||
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
||||
self.clear = true
|
||||
if new_level >= 1300 then
|
||||
self.level = 1300
|
||||
self.grid:clear()
|
||||
self.roll_frames = -150
|
||||
else
|
||||
self.game_over = true
|
||||
end
|
||||
else
|
||||
self.level = math.min(new_level, 1300)
|
||||
end
|
||||
self:advanceBottomRow(-cleared_row_count)
|
||||
end
|
||||
self.grid:applyFourWide()
|
||||
end
|
||||
|
||||
return FourWideGame
|
||||
@@ -1,304 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local MarathonA2Game = GameMode:extend()
|
||||
|
||||
MarathonA2Game.name = "Big A2"
|
||||
MarathonA2Game.hash = "BigA2"
|
||||
MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible roll? Big mode too!"
|
||||
|
||||
|
||||
|
||||
|
||||
function MarathonA2Game:new()
|
||||
self.super:new()
|
||||
self.big_mode = true
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
|
||||
self.grade = 0
|
||||
self.grade_points = 0
|
||||
self.grade_point_decay_counter = 0
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.enable_hold = false
|
||||
self.next_queue_length = 1
|
||||
end
|
||||
|
||||
function MarathonA2Game:getARE()
|
||||
if self.level < 700 then return 27
|
||||
elseif self.level < 800 then return 18
|
||||
else return 14 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getLineARE()
|
||||
if self.level < 600 then return 27
|
||||
elseif self.level < 700 then return 18
|
||||
elseif self.level < 800 then return 14
|
||||
else return 8 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getDasLimit()
|
||||
if self.level < 500 then return 15
|
||||
elseif self.level < 900 then return 9
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getLineClearDelay()
|
||||
if self.level < 500 then return 40
|
||||
elseif self.level < 600 then return 25
|
||||
elseif self.level < 700 then return 16
|
||||
elseif self.level < 800 then return 12
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getLockDelay()
|
||||
if self.level < 900 then return 30
|
||||
else return 17 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getGravity()
|
||||
if (self.level < 30) then return 4/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 < 120) then return 80/256
|
||||
elseif (self.level < 140) then return 96/256
|
||||
elseif (self.level < 160) then return 112/256
|
||||
elseif (self.level < 170) then return 128/256
|
||||
elseif (self.level < 200) then return 144/256
|
||||
elseif (self.level < 220) then return 4/256
|
||||
elseif (self.level < 230) then return 32/256
|
||||
elseif (self.level < 233) then return 64/256
|
||||
elseif (self.level < 236) then return 96/256
|
||||
elseif (self.level < 239) then return 128/256
|
||||
elseif (self.level < 243) then return 160/256
|
||||
elseif (self.level < 247) then return 192/256
|
||||
elseif (self.level < 251) then return 224/256
|
||||
elseif (self.level < 300) then return 1
|
||||
elseif (self.level < 330) then return 2
|
||||
elseif (self.level < 360) then return 3
|
||||
elseif (self.level < 400) then return 4
|
||||
elseif (self.level < 420) then return 5
|
||||
elseif (self.level < 450) then return 4
|
||||
elseif (self.level < 500) then return 3
|
||||
else return 20
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames < 0 then return false end
|
||||
if self.roll_frames > 3694 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function MarathonA2Game:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||
cleared_row_count = cleared_row_count / 2
|
||||
self.level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 and not self.clear then
|
||||
self.clear = true
|
||||
self.grid:clear()
|
||||
self.roll_frames = -150
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
cleared_lines = cleared_lines / 2
|
||||
self:updateGrade(cleared_lines)
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
end
|
||||
|
||||
local grade_point_bonuses = {
|
||||
{10, 20, 40, 50},
|
||||
{10, 20, 30, 40},
|
||||
{10, 20, 30, 40},
|
||||
{10, 15, 30, 40},
|
||||
{10, 15, 20, 40},
|
||||
{5, 15, 20, 30},
|
||||
{5, 10, 20, 30},
|
||||
{5, 10, 15, 30},
|
||||
{5, 10, 15, 30},
|
||||
{5, 10, 15, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
{2, 12, 13, 30},
|
||||
}
|
||||
|
||||
local grade_point_decays = {
|
||||
125, 80, 80, 50, 45, 45, 45,
|
||||
40, 40, 40, 40, 40, 30, 30, 30,
|
||||
20, 20, 20, 20, 20,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
10, 10
|
||||
}
|
||||
|
||||
local combo_multipliers = {
|
||||
{1.0, 1.0, 1.0, 1.0},
|
||||
{1.2, 1.4, 1.5, 1.0},
|
||||
{1.2, 1.5, 1.8, 1.0},
|
||||
{1.4, 1.6, 2.0, 1.0},
|
||||
{1.4, 1.7, 2.2, 1.0},
|
||||
{1.4, 1.8, 2.3, 1.0},
|
||||
{1.4, 1.9, 2.4, 1.0},
|
||||
{1.5, 2.0, 2.5, 1.0},
|
||||
{1.5, 2.1, 2.6, 1.0},
|
||||
{2.0, 2.5, 3.0, 1.0},
|
||||
}
|
||||
|
||||
local grade_conversion = {
|
||||
[0] = 0,
|
||||
1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
|
||||
7, 8, 8, 8, 9, 9, 9, 10, 11, 12,
|
||||
12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||
17
|
||||
}
|
||||
|
||||
function MarathonA2Game:updateGrade(cleared_lines)
|
||||
if self.clear then return end
|
||||
if cleared_lines == 0 then
|
||||
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||
self.grade_point_decay_counter = 0
|
||||
self.grade_points = math.max(0, self.grade_points - 1)
|
||||
end
|
||||
else
|
||||
self.grade_points = self.grade_points + (
|
||||
math.ceil(
|
||||
grade_point_bonuses[self.grade + 1][cleared_lines] *
|
||||
combo_multipliers[math.min(self.combo, 10)][cleared_lines]
|
||||
) * (1 + math.floor(self.level / 250))
|
||||
)
|
||||
if self.grade_points >= 100 and self.grade < 31 then
|
||||
self.grade_points = 0
|
||||
self.grade = self.grade + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getLetterGrade()
|
||||
local grade = grade_conversion[self.grade]
|
||||
if grade < 9 then
|
||||
return tostring(9 - grade)
|
||||
elseif grade < 18 then
|
||||
return "S" .. tostring(grade - 8)
|
||||
end
|
||||
end
|
||||
|
||||
MarathonA2Game.rollOpacityFunction = function(age)
|
||||
if age < 240 then return 1
|
||||
elseif age > 300 then return 0
|
||||
else return 1 - (age - 240) / 60 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:drawGrid(ruleset)
|
||||
if self.clear and not (self.completed or self.game_over) then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
||||
else
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil and self.level < 100 then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game: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("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
function MarathonA2Game:getHighscoreData()
|
||||
return {
|
||||
grade = grade_conversion[self.grade],
|
||||
score = self.score,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function MarathonA2Game:getSectionEndLevel()
|
||||
if self.level >= 900 then return 999
|
||||
else return math.floor(self.level / 100 + 1) * 100 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
return MarathonA2Game
|
||||
@@ -1,326 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
|
||||
|
||||
local SurvivalCKGame = GameMode:extend()
|
||||
|
||||
SurvivalCKGame.name = "Survival CK"
|
||||
SurvivalCKGame.hash = "SurvivalCK"
|
||||
SurvivalCKGame.tagline = "An endurance mode created by CylinderKnot! Watch out for the fading pieces..."
|
||||
|
||||
function SurvivalCKGame:new()
|
||||
SurvivalCKGame.super:new()
|
||||
|
||||
self.garbage = 0
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.grade = 0
|
||||
self.level = 0
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
|
||||
self.coolregret_timer = 0
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getARE()
|
||||
if self.level < 100 then return 15
|
||||
elseif self.level < 200 then return 14
|
||||
elseif self.level < 300 then return 13
|
||||
elseif self.level < 400 then return 12
|
||||
elseif self.level < 500 then return 11
|
||||
elseif self.level < 600 then return 10
|
||||
elseif self.level < 700 then return 9
|
||||
elseif self.level < 800 then return 8
|
||||
elseif self.level < 900 then return 7
|
||||
elseif self.level < 1000 then return 6
|
||||
elseif self.level < 2500 then return 5
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
|
||||
function SurvivalCKGame:getLineARE()
|
||||
return SurvivalCKGame:getARE()
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getDasLimit()
|
||||
if self.level < 700 then return 10
|
||||
elseif self.level < 900 then return 9
|
||||
elseif self.level < 1100 then return 8
|
||||
elseif self.level < 1300 then return 7
|
||||
elseif self.level < 1600 then return 6
|
||||
else return 5 end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getLineClearDelay()
|
||||
if self.level < 100 then return 10
|
||||
elseif self.level < 200 then return 8
|
||||
elseif self.level < 300 then return 7
|
||||
elseif self.level < 400 then return 6
|
||||
else return 5 end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getLockDelay()
|
||||
if self.level < 600 then return 20
|
||||
elseif self.level < 700 then return 19
|
||||
elseif self.level < 800 then return 18
|
||||
elseif self.level < 900 then return 17
|
||||
elseif self.level < 1000 then return 16
|
||||
elseif self.level < 1200 then return 15
|
||||
elseif self.level < 1400 then return 14
|
||||
elseif self.level < 1700 then return 13
|
||||
elseif self.level < 2100 then return 12
|
||||
elseif self.level < 2200 then return 11
|
||||
elseif self.level < 2300 then return 10
|
||||
elseif self.level < 2400 then return 9
|
||||
elseif self.level < 2500 then return 8
|
||||
else return 15 end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getGarbageLimit()
|
||||
if self.level < 1000 then return 20
|
||||
elseif self.level < 1100 then return 17
|
||||
elseif self.level < 1200 then return 14
|
||||
elseif self.level < 1300 then return 11
|
||||
else return 8 end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getRegretTime()
|
||||
if self.level < 500 then return frameTime(0,55)
|
||||
elseif self.level < 1000 then return frameTime(0,50)
|
||||
elseif self.level < 1500 then return frameTime(0,40)
|
||||
elseif self.level < 2000 then return frameTime(0,35)
|
||||
else return frameTime(0,30) end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getNextPiece(ruleset)
|
||||
return {
|
||||
skin = self.level >= 2000 and "bone" or "2tie",
|
||||
shape = self.randomizer:nextPiece(),
|
||||
orientation = ruleset:getDefaultOrientation(),
|
||||
}
|
||||
end
|
||||
|
||||
local torikan_times = {300, 330, 360, 390, 420, 450, 478, 504, 528, 550, 570}
|
||||
|
||||
function SurvivalCKGame:hitTorikan(old_level, new_level)
|
||||
for i = 1, 11 do
|
||||
if old_level < (900 + i * 100) and new_level >= (900 + i * 100) and self.frames > torikan_times[i] * 60 then
|
||||
self.level = 900 + i * 100
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function SurvivalCKGame:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames < 0 then
|
||||
if self.roll_frames + 1 == 0 then
|
||||
switchBGM("credit_roll", "gm3")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
elseif self.roll_frames > 3238 then
|
||||
switchBGM(nil)
|
||||
if self.grade ~= 20 then self.grade = self.grade + 1 end
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SurvivalCKGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = self.level + cleared_row_count * 2
|
||||
self:updateSectionTimes(self.level, new_level)
|
||||
if new_level >= 2500 or self:hitTorikan(self.level, new_level) then
|
||||
self.clear = true
|
||||
if new_level >= 2500 then
|
||||
self.level = 2500
|
||||
self.grid:clear()
|
||||
self.big_mode = true
|
||||
self.roll_frames = -150
|
||||
end
|
||||
else
|
||||
self.level = math.min(new_level, 2500)
|
||||
end
|
||||
self:advanceBottomRow(-cleared_row_count)
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:onPieceLock(piece, cleared_row_count)
|
||||
if cleared_row_count == 0 then self:advanceBottomRow(1) end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:updateSectionTimes(old_level, new_level)
|
||||
if math.floor(old_level / 100) < math.floor(new_level / 100) then
|
||||
local section = math.floor(old_level / 100) + 1
|
||||
section_time = self.frames - self.section_start_time
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
if section_time <= self:getRegretTime(self.level) then
|
||||
self.grade = self.grade + 1
|
||||
else
|
||||
self.coolregret_message = "REGRET!!"
|
||||
self.coolregret_timer = 300
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:advanceBottomRow(dx)
|
||||
if self.level >= 1000 and self.level < 1500 then
|
||||
self.garbage = math.max(self.garbage + dx, 0)
|
||||
if self.garbage >= self:getGarbageLimit() then
|
||||
self.grid:copyBottomRow()
|
||||
self.garbage = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:drawGrid()
|
||||
if self.level >= 1500 and self.level < 1600 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction1)
|
||||
elseif self.level >= 1600 and self.level < 1700 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction2)
|
||||
elseif self.level >= 1700 and self.level < 1800 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction3)
|
||||
elseif self.level >= 1800 and self.level < 1900 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction4)
|
||||
elseif self.level >= 1900 and self.level < 2000 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction5)
|
||||
else
|
||||
self.grid:draw()
|
||||
end
|
||||
end
|
||||
|
||||
-- screw trying to make this work efficiently
|
||||
-- lua function variables are so garbage
|
||||
|
||||
SurvivalCKGame.rollOpacityFunction1 = function(age)
|
||||
if age < 420 then return 1
|
||||
elseif age > 480 then return 0
|
||||
else return 1 - (age - 420) / 60 end
|
||||
end
|
||||
|
||||
SurvivalCKGame.rollOpacityFunction2 = function(age)
|
||||
if age < 360 then return 1
|
||||
elseif age > 420 then return 0
|
||||
else return 1 - (age - 360) / 60 end
|
||||
end
|
||||
|
||||
SurvivalCKGame.rollOpacityFunction3 = function(age)
|
||||
if age < 300 then return 1
|
||||
elseif age > 360 then return 0
|
||||
else return 1 - (age - 300) / 60 end
|
||||
end
|
||||
|
||||
SurvivalCKGame.rollOpacityFunction4 = function(age)
|
||||
if age < 240 then return 1
|
||||
elseif age > 300 then return 0
|
||||
else return 1 - (age - 240) / 60 end
|
||||
end
|
||||
|
||||
SurvivalCKGame.rollOpacityFunction5 = function(age)
|
||||
if age < 180 then return 1
|
||||
elseif age > 240 then return 0
|
||||
else return 1 - (age - 180) / 60 end
|
||||
end
|
||||
|
||||
local master_grades = { "M", "MK", "MV", "MO", "MM" }
|
||||
|
||||
function SurvivalCKGame:getLetterGrade()
|
||||
if self.grade == 0 then
|
||||
return "1"
|
||||
elseif self.grade < 10 then
|
||||
return "S" .. tostring(self.grade)
|
||||
elseif self.grade < 21 then
|
||||
return "m" .. tostring(self.grade - 9)
|
||||
elseif self.grade < 26 then
|
||||
return master_grades[self.grade - 20]
|
||||
else
|
||||
return "GM"
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:drawScoringInfo()
|
||||
SurvivalCKGame.super.drawScoringInfo(self)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
local text_x = config["side_next"] and 320 or 240
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
|
||||
if (self.coolregret_timer > 0) then
|
||||
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||
self.coolregret_timer = self.coolregret_timer - 1
|
||||
end
|
||||
|
||||
local current_section = math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self:getLetterGrade(self.grade), text_x, 140, 90, "left")
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
||||
if self.clear then
|
||||
love.graphics.printf(self.level, text_x, 370, 50, "right")
|
||||
else
|
||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getHighscoreData()
|
||||
return {
|
||||
grade = self.grade,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getSectionEndLevel()
|
||||
return math.floor(self.level / 100 + 1) * 100
|
||||
end
|
||||
|
||||
function SurvivalCKGame:getBackground()
|
||||
return math.min(math.floor(self.level / 100), 19)
|
||||
end
|
||||
|
||||
return SurvivalCKGame
|
||||
119
tetris/modes/credits_a3.lua
Normal file
119
tetris/modes/credits_a3.lua
Normal file
@@ -0,0 +1,119 @@
|
||||
require 'funcs'
|
||||
|
||||
local IntervalTrainingMode = require 'tetris.modes.interval_training'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local CreditsA3Game = IntervalTrainingMode:extend()
|
||||
|
||||
CreditsA3Game.name = "Credits A3"
|
||||
CreditsA3Game.hash = "CreditsA3"
|
||||
CreditsA3Game.tagline = "How consistently can you clear the Ti M-roll?"
|
||||
|
||||
function CreditsA3Game:new()
|
||||
CreditsA3Game.super:new()
|
||||
self.section_time_limit = 3238
|
||||
self.norm = 0
|
||||
self.section = 0
|
||||
end
|
||||
|
||||
function CreditsA3Game:advanceOneFrame(inputs, ruleset)
|
||||
if self.frames == 0 then
|
||||
switchBGM("credit_roll", "gm3")
|
||||
end
|
||||
if self.roll_frames > 0 then
|
||||
self.roll_frames = self.roll_frames - 1
|
||||
if self.roll_frames == 0 then
|
||||
-- reset
|
||||
self.norm = 0
|
||||
self.frames = 0
|
||||
self.grid:clear()
|
||||
switchBGM("credit_roll", "gm3")
|
||||
self:initializeOrHold(inputs, ruleset)
|
||||
else
|
||||
return false
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
if self:getSectionTime() >= self.section_time_limit then
|
||||
self.norm = self.norm + 16
|
||||
self.piece = nil
|
||||
if self.norm >= 60 then
|
||||
self.section = self.section + 1
|
||||
self.roll_frames = 150
|
||||
else
|
||||
self.game_over = true
|
||||
switchBGM(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function CreditsA3Game:onPieceEnter()
|
||||
-- do nothing
|
||||
end
|
||||
|
||||
function CreditsA3Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
self.norm = self.norm + (cleared_row_count == 4 and 10 or cleared_row_count)
|
||||
end
|
||||
end
|
||||
|
||||
CreditsA3Game.rollOpacityFunction = function(age)
|
||||
if age > 4 then return 0
|
||||
else return 1 - age / 4 end
|
||||
end
|
||||
|
||||
function CreditsA3Game:drawGrid(ruleset)
|
||||
if not self.game_over and self.roll_frames < 30 then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
||||
else
|
||||
self.grid:draw()
|
||||
end
|
||||
end
|
||||
|
||||
function CreditsA3Game:drawScoringInfo()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||
love.graphics.printf("NORM", 240, 320, 40, "left")
|
||||
|
||||
self:drawSectionTimesWithSplits(self.section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
-- draw time left, flash red if necessary
|
||||
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
||||
|
||||
if not self.game_over and not self.clear and time_left < sp(0,10) and time_left % 4 < 2 then
|
||||
if self.norm >= 44 then
|
||||
love.graphics.setColor(0.3, 1, 0.3, 1) -- flash green if goal has been cleared
|
||||
else
|
||||
love.graphics.setColor(1, 0.3, 0.3, 1) -- otherwise flash red
|
||||
end
|
||||
end
|
||||
|
||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.norm, 240, 340, 40, "right")
|
||||
if self.game_over or self.roll_frames > 0 then
|
||||
love.graphics.printf("60", 240, 370, 40, "right")
|
||||
else
|
||||
love.graphics.printf("44", 240, 370, 40, "right")
|
||||
end
|
||||
end
|
||||
|
||||
function CreditsA3Game:getBackground()
|
||||
return self.section
|
||||
end
|
||||
|
||||
return CreditsA3Game
|
||||
@@ -11,6 +11,9 @@ DemonModeGame.name = "Demon Mode"
|
||||
DemonModeGame.hash = "DemonMode"
|
||||
DemonModeGame.tagline = "Can you handle the ludicrous speed past level 20?"
|
||||
|
||||
|
||||
|
||||
|
||||
function DemonModeGame:new()
|
||||
DemonModeGame.super:new()
|
||||
self.roll_frames = 0
|
||||
@@ -26,9 +29,6 @@ function DemonModeGame:new()
|
||||
self.enable_hold = true
|
||||
self.lock_drop = true
|
||||
self.next_queue_length = 3
|
||||
if math.random() < 1/6.66 then
|
||||
self.rpc_details = "Suffering"
|
||||
end
|
||||
end
|
||||
|
||||
function DemonModeGame:getARE()
|
||||
@@ -159,27 +159,18 @@ function DemonModeGame:updateSectionTimes(old_level, new_level)
|
||||
end
|
||||
elseif old_level < 100 then
|
||||
-- If section time is under cutoff, skip to level 500.
|
||||
if self.frames < frameTime(1,00) then
|
||||
if self.frames < sp(1,00) then
|
||||
self.level = 500
|
||||
self.grade = 5
|
||||
self.section_tries = 0
|
||||
self.section_tetris_count = 0
|
||||
else
|
||||
self.level = math.min(new_level, 2500)
|
||||
self.skip_failed = true
|
||||
self.grade = self.grade + 1
|
||||
end
|
||||
-- record new section
|
||||
section_time = self.frames - self.section_start_time
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
else
|
||||
self.level = math.min(new_level, 2500)
|
||||
if self.skip_failed and new_level >= 500 then
|
||||
self.level = 500
|
||||
self.game_over = true
|
||||
end
|
||||
self.grade = math.min(self.grade + 1, 4)
|
||||
end
|
||||
else
|
||||
self.level = math.min(new_level, 2500)
|
||||
@@ -187,17 +178,16 @@ function DemonModeGame:updateSectionTimes(old_level, new_level)
|
||||
end
|
||||
|
||||
function DemonModeGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -236,10 +226,10 @@ function DemonModeGame:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
if self.grade ~= 0 then love.graphics.printf("GRADE", 240, 120, 40, "left") end
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
@@ -262,7 +252,7 @@ function DemonModeGame:getHighscoreData()
|
||||
end
|
||||
|
||||
function DemonModeGame:getBackground()
|
||||
return math.min(math.floor(self.level / 100), 19)
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
return DemonModeGame
|
||||
|
||||
@@ -40,12 +40,9 @@ function GameMode:new()
|
||||
self.draw_section_times = false
|
||||
self.draw_secondary_section_times = false
|
||||
self.big_mode = false
|
||||
self.rpc_details = "In game"
|
||||
-- variables related to configurable parameters
|
||||
self.drop_locked = false
|
||||
self.hard_drop_locked = false
|
||||
self.lock_on_soft_drop = false
|
||||
self.lock_on_hard_drop = false
|
||||
self.hold_queue = nil
|
||||
self.held = false
|
||||
self.section_start_time = 0
|
||||
@@ -53,6 +50,13 @@ function GameMode:new()
|
||||
self.secondary_section_times = { [0] = 0 }
|
||||
end
|
||||
|
||||
function GameMode:initialize()
|
||||
-- after all the variables are initialized, run initialization procedures
|
||||
for i = 1, 30 do
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:getARR() return 1 end
|
||||
function GameMode:getDropSpeed() return 1 end
|
||||
function GameMode:getARE() return 25 end
|
||||
@@ -62,7 +66,6 @@ function GameMode:getLineClearDelay() return 40 end
|
||||
function GameMode:getDasLimit() return 15 end
|
||||
|
||||
function GameMode:getNextPiece(ruleset)
|
||||
|
||||
return {
|
||||
skin = "2tie",
|
||||
shape = self.randomizer:nextPiece(),
|
||||
@@ -72,12 +75,9 @@ end
|
||||
|
||||
function GameMode:initialize(ruleset)
|
||||
-- generate next queue
|
||||
self:new()
|
||||
for i = 1, self.next_queue_length do
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
|
||||
self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true, false})[config.gamesettings.manlock]
|
||||
end
|
||||
|
||||
function GameMode:update(inputs, ruleset)
|
||||
@@ -91,10 +91,20 @@ function GameMode:update(inputs, ruleset)
|
||||
if self.completed then return end
|
||||
|
||||
-- advance one frame
|
||||
if self:advanceOneFrame(inputs) == false then return end
|
||||
if self:advanceOneFrame(inputs, ruleset) == false then return end
|
||||
|
||||
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
|
||||
|
||||
-- set attempt flags
|
||||
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end
|
||||
if
|
||||
inputs["rotate_left"] or inputs["rotate_right"] or
|
||||
inputs["rotate_left2"] or inputs["rotate_right2"] or
|
||||
inputs["rotate_180"]
|
||||
then
|
||||
self:onAttemptPieceRotate(self.piece)
|
||||
end
|
||||
|
||||
if self.piece == nil then
|
||||
self:processDelays(inputs, ruleset)
|
||||
else
|
||||
@@ -131,7 +141,7 @@ function GameMode:update(inputs, ruleset)
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
not self.hard_drop_locked then
|
||||
self:onHardDrop(piece_dy)
|
||||
if self.lock_on_hard_drop then
|
||||
if self.instant_hard_drop then
|
||||
self.piece.locked = true
|
||||
end
|
||||
end
|
||||
@@ -140,7 +150,7 @@ function GameMode:update(inputs, ruleset)
|
||||
self:onSoftDrop(piece_dy)
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
not self.drop_locked and
|
||||
self.lock_on_soft_drop
|
||||
self.instant_soft_drop
|
||||
then
|
||||
self.piece.locked = true
|
||||
end
|
||||
@@ -152,6 +162,10 @@ function GameMode:update(inputs, ruleset)
|
||||
|
||||
local cleared_row_count = self.grid:getClearedRowCount()
|
||||
|
||||
if self.big_mode then
|
||||
cleared_row_count = cleared_row_count / 2
|
||||
end
|
||||
|
||||
self:onPieceLock(self.piece, cleared_row_count)
|
||||
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
|
||||
|
||||
@@ -194,6 +208,8 @@ end
|
||||
|
||||
-- event functions
|
||||
function GameMode:whilePieceActive() end
|
||||
function GameMode:onAttemptPieceMove(piece) end
|
||||
function GameMode:onAttemptPieceRotate(piece) end
|
||||
function GameMode:onPieceLock(piece, cleared_row_count) end
|
||||
function GameMode:onLineClear(cleared_row_count) end
|
||||
function GameMode:onPieceEnter() end
|
||||
@@ -211,30 +227,66 @@ function GameMode:onGameOver()
|
||||
switchBGM(nil)
|
||||
end
|
||||
|
||||
function GameMode:chargeDAS(inputs)
|
||||
if inputs[self.das.direction] == true then
|
||||
local das_frames = self.das.frames + 1
|
||||
if das_frames >= self:getDasLimit() then
|
||||
if self.das.direction == "left" then
|
||||
self.move = (self:getARR() == 0 and "speed" or "") .. "left"
|
||||
self.das.frames = self:getDasLimit() - self:getARR()
|
||||
elseif self.das.direction == "right" then
|
||||
self.move = (self:getARR() == 0 and "speed" or "") .. "right"
|
||||
self.das.frames = self:getDasLimit() - self:getARR()
|
||||
end
|
||||
else
|
||||
self.move = "none"
|
||||
self.das.frames = das_frames
|
||||
-- DAS functions
|
||||
|
||||
function GameMode:startRightDAS()
|
||||
self.move = "right"
|
||||
self.das = { direction = "right", frames = 0 }
|
||||
if self:getDasLimit() == 0 then
|
||||
self:continueDAS()
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:startLeftDAS()
|
||||
self.move = "left"
|
||||
self.das = { direction = "left", frames = 0 }
|
||||
if self:getDasLimit() == 0 then
|
||||
self:continueDAS()
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:continueDAS()
|
||||
local das_frames = self.das.frames + 1
|
||||
if das_frames >= self:getDasLimit() then
|
||||
if self.das.direction == "left" then
|
||||
self.move = (self:getARR() == 0 and "speed" or "") .. "left"
|
||||
self.das.frames = self:getDasLimit() - self:getARR()
|
||||
elseif self.das.direction == "right" then
|
||||
self.move = (self:getARR() == 0 and "speed" or "") .. "right"
|
||||
self.das.frames = self:getDasLimit() - self:getARR()
|
||||
end
|
||||
elseif inputs["right"] == true then
|
||||
self.move = "right"
|
||||
self.das = { direction = "right", frames = 0 }
|
||||
elseif inputs["left"] == true then
|
||||
self.move = "left"
|
||||
self.das = { direction = "left", frames = 0 }
|
||||
else
|
||||
self.move = "none"
|
||||
self.das = { direction = "none", frames = -1 }
|
||||
self.das.frames = das_frames
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:stopDAS()
|
||||
self.move = "none"
|
||||
self.das = { direction = "none", frames = -1 }
|
||||
end
|
||||
|
||||
function GameMode:chargeDAS(inputs)
|
||||
if config["das_last_key"] then
|
||||
if inputs["right"] == true and self.das.direction ~= "right" and not self.prev_inputs["right"] then
|
||||
self:startRightDAS()
|
||||
elseif inputs["left"] == true and self.das.direction ~= "left" and not self.prev_inputs["left"] then
|
||||
self:startLeftDAS()
|
||||
elseif inputs[self.das.direction] == true then
|
||||
self:continueDAS()
|
||||
else
|
||||
self:stopDAS()
|
||||
end
|
||||
else -- default behaviour, das first key pressed
|
||||
if inputs[self.das.direction] == true then
|
||||
self:continueDAS()
|
||||
elseif inputs["right"] == true then
|
||||
self:startRightDAS()
|
||||
elseif inputs["left"] == true then
|
||||
self:startLeftDAS()
|
||||
else
|
||||
self:stopDAS()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -346,12 +398,11 @@ function GameMode:drawGhostPiece(ruleset)
|
||||
end
|
||||
|
||||
function GameMode:drawNextQueue(ruleset)
|
||||
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
function drawPiece(piece, skin, offsets, pos_x, pos_y)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = offset.x + ruleset.spawn_positions[piece].x
|
||||
local y = offset.y + 4.7
|
||||
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
|
||||
local x = ruleset.spawn_positions[piece].x + offset.x
|
||||
local y = ruleset.spawn_positions[piece].y + offset.y
|
||||
love.graphics.draw(blocks[skin][piece], pos_x+x*16, pos_y+y*16)
|
||||
end
|
||||
end
|
||||
for i = 1, self.next_queue_length do
|
||||
@@ -366,8 +417,7 @@ function GameMode:drawNextQueue(ruleset)
|
||||
end
|
||||
end
|
||||
if self.hold_queue ~= nil then
|
||||
local hold_color = self.held and 0.6 or 1
|
||||
self:setHoldOpacity(1, hold_color)
|
||||
self:setHoldOpacity()
|
||||
drawPiece(
|
||||
self.hold_queue.shape,
|
||||
self.hold_queue.skin,
|
||||
@@ -378,16 +428,8 @@ function GameMode:drawNextQueue(ruleset)
|
||||
return false
|
||||
end
|
||||
|
||||
function GameMode:setNextOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
end
|
||||
function GameMode:setHoldOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
end
|
||||
function GameMode:setNextOpacity(i) love.graphics.setColor(1, 1, 1, 1) end
|
||||
function GameMode:setHoldOpacity() love.graphics.setColor(1, 1, 1, 1) end
|
||||
|
||||
function GameMode:drawScoringInfo()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
@@ -402,8 +444,7 @@ function GameMode:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs) ..
|
||||
self.drop_bonus
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
@@ -413,7 +454,7 @@ end
|
||||
function GameMode:drawSectionTimes(current_section)
|
||||
local section_x = 530
|
||||
|
||||
for section, time in pairs(self.section_times) do
|
||||
for section, time in ipairs(self.section_times) do
|
||||
if section > 0 then
|
||||
love.graphics.printf(formatTime(time), section_x, 40 + 20 * section, 90, "left")
|
||||
end
|
||||
@@ -422,7 +463,7 @@ function GameMode:drawSectionTimes(current_section)
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), section_x, 40 + 20 * current_section, 90, "left")
|
||||
end
|
||||
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section, section_colour_function)
|
||||
local section_x = 530
|
||||
local section_secondary_x = 440
|
||||
|
||||
@@ -433,6 +474,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
end
|
||||
|
||||
for section, time in pairs(self.secondary_section_times) do
|
||||
if self.section_colour_function then
|
||||
love.graphics.setColor(self:section_colour_function(section))
|
||||
end
|
||||
if section > 0 then
|
||||
love.graphics.printf(formatTime(time), section_secondary_x, 40 + 20 * section, 90, "left")
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'funcs'
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local IntervalTrainingGame = GameMode:extend()
|
||||
|
||||
@@ -15,12 +15,11 @@ IntervalTrainingGame.tagline = "Can you clear the time hurdles when the game goe
|
||||
|
||||
|
||||
function IntervalTrainingGame:new()
|
||||
self.level = 0
|
||||
IntervalTrainingGame.super:new()
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_time_limit = 1800
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.lock_drop = true
|
||||
@@ -28,12 +27,6 @@ function IntervalTrainingGame:new()
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:initialize(ruleset)
|
||||
self.section_time_limit = 1800
|
||||
if ruleset.world then self.section_time_limit = 37 * 60 end
|
||||
self.super.initialize(self, ruleset)
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:getARE()
|
||||
return 6
|
||||
end
|
||||
@@ -68,6 +61,7 @@ function IntervalTrainingGame:advanceOneFrame()
|
||||
if self.roll_frames > 2968 then
|
||||
self.completed = true
|
||||
end
|
||||
return false
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
if self:getSectionTime() >= self.section_time_limit then
|
||||
@@ -78,15 +72,14 @@ function IntervalTrainingGame:advanceOneFrame()
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:onLineClear(cleared_row_count)
|
||||
local cleared_level_bonus = {1, 2, 4, 6}
|
||||
if not self.clear then
|
||||
local new_level = self.level + cleared_level_bonus[cleared_row_count]
|
||||
local new_level = self.level + cleared_row_count
|
||||
self:updateSectionTimes(self.level, new_level)
|
||||
self.level = math.min(new_level, 999)
|
||||
if self.level == 999 then
|
||||
@@ -104,10 +97,12 @@ function IntervalTrainingGame:updateSectionTimes(old_level, new_level)
|
||||
-- record new section
|
||||
table.insert(self.section_times, self:getSectionTime())
|
||||
self.section_start_time = self.frames
|
||||
else
|
||||
self.level = math.min(new_level, 999)
|
||||
end
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:drawGrid()
|
||||
function IntervalTrainingGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
@@ -125,26 +120,25 @@ function IntervalTrainingGame:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
if not self.clear then love.graphics.printf("TIME LEFT", 240, 250, 80, "left") end
|
||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
local current_section = math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
|
||||
-- draw time left, flash red if necessary
|
||||
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
||||
if not self.game_over and time_left < frameTime(0,10) and time_left % 4 < 2 then
|
||||
if not self.game_over and not self.clear and time_left < sp(0,10) and time_left % 4 < 2 then
|
||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||
end
|
||||
if not self.clear then love.graphics.printf(formatTime(time_left), 240, 270, 160, "left") end
|
||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
end
|
||||
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local KonohaRandomizer = require 'tetris.randomizers.bag_konoha'
|
||||
|
||||
local KonohaGame = GameMode:extend()
|
||||
|
||||
KonohaGame.name = "All Clear A4"
|
||||
KonohaGame.hash = "AllClearA4"
|
||||
KonohaGame.tagline = "Get as many bravos as you can under the time limit!"
|
||||
|
||||
function KonohaGame:new()
|
||||
KonohaGame.super:new()
|
||||
|
||||
self.randomizer = KonohaRandomizer()
|
||||
self.bravos = 0
|
||||
self.last_bonus_amount = 0
|
||||
self.last_bonus_display_time = 0
|
||||
self.time_limit = 10800
|
||||
self.big_mode = true
|
||||
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function KonohaGame:getARE()
|
||||
if self.level < 300 then return 30
|
||||
elseif self.level < 400 then return 25
|
||||
elseif self.level < 500 then return 20
|
||||
elseif self.level < 600 then return 17
|
||||
elseif self.level < 800 then return 15
|
||||
elseif self.level < 900 then return 13
|
||||
elseif self.level < 1000 then return 10
|
||||
elseif self.level < 1300 then return 8
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function KonohaGame:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function KonohaGame:getDasLimit()
|
||||
if self.level < 500 then return 10
|
||||
elseif self.level < 800 then return 9
|
||||
elseif self.level < 1000 then return 8
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
function KonohaGame:getLineClearDelay()
|
||||
if self.level < 200 then return 14
|
||||
elseif self.level < 500 then return 9
|
||||
elseif self.level < 800 then return 8
|
||||
elseif self.level < 1000 then return 7
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function KonohaGame:getLockDelay()
|
||||
if self.level < 500 then return 30
|
||||
elseif self.level < 600 then return 25
|
||||
elseif self.level < 700 then return 23
|
||||
elseif self.level < 800 then return 20
|
||||
elseif self.level < 900 then return 17
|
||||
elseif self.level < 1000 then return 15
|
||||
elseif self.level < 1200 then return 13
|
||||
elseif self.level < 1300 then return 10
|
||||
else return 8 end
|
||||
end
|
||||
|
||||
function KonohaGame:getGravity()
|
||||
if (self.level < 30) then return 4/256
|
||||
elseif (self.level < 35) then return 8/256
|
||||
elseif (self.level < 40) then return 12/256
|
||||
elseif (self.level < 50) then return 16/256
|
||||
elseif (self.level < 60) then return 32/256
|
||||
elseif (self.level < 70) then return 48/256
|
||||
elseif (self.level < 80) then return 64/256
|
||||
elseif (self.level < 90) then return 128/256
|
||||
elseif (self.level < 100) then return 192/256
|
||||
elseif (self.level < 120) then return 1
|
||||
elseif (self.level < 140) then return 2
|
||||
elseif (self.level < 160) then return 3
|
||||
elseif (self.level < 170) then return 4
|
||||
elseif (self.level < 200) then return 5
|
||||
else return 20 end
|
||||
end
|
||||
|
||||
function KonohaGame:getSection()
|
||||
return math.floor(level / 100) + 1
|
||||
end
|
||||
|
||||
function KonohaGame:getSectionEndLevel()
|
||||
return math.floor(self.level / 100 + 1) * 100
|
||||
end
|
||||
|
||||
function KonohaGame:advanceOneFrame()
|
||||
if self.ready_frames == 0 then
|
||||
self.time_limit = self.time_limit - 1
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
if self.time_limit <= 0 then
|
||||
self.game_over = true
|
||||
end
|
||||
self.last_bonus_display_time = self.last_bonus_display_time - 1
|
||||
end
|
||||
|
||||
function KonohaGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99) and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function KonohaGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil and self.level < 100 then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
local cleared_row_levels = {2, 4, 6, 12}
|
||||
local bravo_bonus = {300, 480, 660, 900}
|
||||
local non_bravo_bonus = {0, 0, 20, 40}
|
||||
local bravo_ot_bonus = {0, 60, 120, 180}
|
||||
|
||||
function KonohaGame:onLineClear(cleared_row_count)
|
||||
local oldtime = self.time_limit
|
||||
|
||||
self.level = self.level + cleared_row_levels[cleared_row_count / 2]
|
||||
if self.grid:checkForBravo(cleared_row_count) then
|
||||
self.bravos = self.bravos + 1
|
||||
if self.level < 1000 then self.time_limit = self.time_limit + bravo_bonus[cleared_row_count / 2]
|
||||
else self.time_limit = self.time_limit + bravo_ot_bonus[cleared_row_count / 2]
|
||||
end
|
||||
if self.bravos == 11 then self.randomizer.allowrepeat = true end
|
||||
elseif self.level < 1000 then
|
||||
self.time_limit = self.time_limit + non_bravo_bonus[cleared_row_count / 2]
|
||||
end
|
||||
|
||||
local bonus = self.time_limit - oldtime
|
||||
if bonus > 0 then
|
||||
self.last_bonus_amount = bonus
|
||||
self.last_bonus_display_time = 120
|
||||
end
|
||||
end
|
||||
|
||||
function KonohaGame:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
function KonohaGame: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("TIME LIMIT", 240, 120, 120, "left")
|
||||
love.graphics.printf("BRAVOS", 240, 200, 50, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if not self.game_over and self.time_limit < frameTime(0,10) and self.time_limit % 4 < 2 then
|
||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||
end
|
||||
love.graphics.printf(formatTime(self.time_limit), 240, 140, 120, "right")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
if self.last_bonus_display_time > 0 then
|
||||
love.graphics.printf("+"..formatTime(self.last_bonus_amount), 240, 160, 120, "right")
|
||||
end
|
||||
love.graphics.printf(self.bravos, 240, 220, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 50, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 50, "right")
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
function KonohaGame:getHighscoreData()
|
||||
return {
|
||||
bravos = self.bravos,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
return KonohaGame
|
||||
@@ -36,8 +36,6 @@ function Marathon2020Game:new()
|
||||
self.grade_points = 0
|
||||
self.grade_point_decay_counter = 0
|
||||
self.max_grade_points = 0
|
||||
|
||||
self.cool_timer = 0
|
||||
end
|
||||
|
||||
function Marathon2020Game:getARE()
|
||||
@@ -153,11 +151,11 @@ function Marathon2020Game:advanceOneFrame()
|
||||
end
|
||||
|
||||
local cool_cutoffs = {
|
||||
frameTime(0,45,00), frameTime(0,41,50), frameTime(0,38,50), frameTime(0,35,00), frameTime(0,32,50),
|
||||
frameTime(0,29,20), frameTime(0,27,20), frameTime(0,24,80), frameTime(0,22,80), frameTime(0,20,60),
|
||||
frameTime(0,19,60), frameTime(0,19,40), frameTime(0,19,40), frameTime(0,18,40), frameTime(0,18,20),
|
||||
frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20), frameTime(0,16,20),
|
||||
frameTime(0,15,20)
|
||||
sp(0,45,00), sp(0,41,50), sp(0,38,50), sp(0,35,00), sp(0,32,50),
|
||||
sp(0,29,20), sp(0,27,20), sp(0,24,80), sp(0,22,80), sp(0,20,60),
|
||||
sp(0,19,60), sp(0,19,40), sp(0,19,40), sp(0,18,40), sp(0,18,20),
|
||||
sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20), sp(0,16,20),
|
||||
sp(0,15,20)
|
||||
}
|
||||
|
||||
local levels_for_cleared_rows = { 1, 2, 4, 6 }
|
||||
@@ -286,11 +284,11 @@ function Marathon2020Game:sectionPassed(old_level, new_level)
|
||||
end
|
||||
|
||||
function Marathon2020Game:checkTorikan(section)
|
||||
if section == 5 and self.frames < frameTime(6,00,00) then self.torikan_passed[500] = true end
|
||||
if section == 9 and self.frames < frameTime(8,30,00) then self.torikan_passed[900] = true end
|
||||
if section == 10 and self.frames < frameTime(8,45,00) then self.torikan_passed[1000] = true end
|
||||
if section == 15 and self.frames < frameTime(11,30,00) then self.torikan_passed[1500] = true end
|
||||
if section == 19 and self.frames < frameTime(13,15,00) then self.torikan_passed[1900] = true end
|
||||
if section == 5 and self.frames < sp(6,00,00) then self.torikan_passed[500] = true end
|
||||
if section == 9 and self.frames < sp(8,30,00) then self.torikan_passed[900] = true end
|
||||
if section == 10 and self.frames < sp(8,45,00) then self.torikan_passed[1000] = true end
|
||||
if section == 15 and self.frames < sp(11,30,00) then self.torikan_passed[1500] = true end
|
||||
if section == 19 and self.frames < sp(13,15,00) then self.torikan_passed[1900] = true end
|
||||
end
|
||||
|
||||
function Marathon2020Game:checkClear(level)
|
||||
@@ -325,11 +323,10 @@ function Marathon2020Game:checkClear(level)
|
||||
end
|
||||
|
||||
function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
function sectionCool()
|
||||
function sectionCool(section)
|
||||
self.section_cool_count = self.section_cool_count + 1
|
||||
self.delay_level = math.min(20, self.delay_level + 1)
|
||||
if section < 10 then self.delay_level = math.min(20, self.delay_level + 1) end
|
||||
table.insert(self.section_status, "cool")
|
||||
self.cool_timer = 300
|
||||
end
|
||||
|
||||
local section = getSectionForLevel(old_level)
|
||||
@@ -346,7 +343,7 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
|
||||
if section > 4 then self.delay_level = math.min(20, self.delay_level + 1) end
|
||||
if section > 5 then self.delay_level = math.min(20, self.delay_level + 1) end
|
||||
self:checkTorikan(section)
|
||||
self:checkClear(new_level)
|
||||
|
||||
@@ -355,11 +352,11 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
|
||||
self.secondary_section_times[section] < cool_cutoffs[section]
|
||||
) then
|
||||
sectionCool()
|
||||
sectionCool(section)
|
||||
elseif self.section_status[section - 1] == "cool" then
|
||||
table.insert(self.section_status, "none")
|
||||
elseif section <= 19 and self.secondary_section_times[section] < cool_cutoffs[section] then
|
||||
sectionCool()
|
||||
sectionCool(section)
|
||||
else
|
||||
table.insert(self.section_status, "none")
|
||||
end
|
||||
@@ -420,6 +417,14 @@ function Marathon2020Game:drawGrid()
|
||||
end
|
||||
end
|
||||
|
||||
function Marathon2020Game:sectionColourFunction(section)
|
||||
if self.section_status[section] == "cool" then
|
||||
return { 0, 1, 0, 1 }
|
||||
else
|
||||
return { 1, 1, 1, 1 }
|
||||
end
|
||||
end
|
||||
|
||||
function Marathon2020Game:drawScoringInfo()
|
||||
Marathon2020Game.super.drawScoringInfo(self)
|
||||
|
||||
@@ -431,12 +436,7 @@ function Marathon2020Game:drawScoringInfo()
|
||||
love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
|
||||
self:drawSectionTimesWithSecondary(current_section)
|
||||
|
||||
if (self.cool_timer > 0) then
|
||||
love.graphics.printf("COOL!!", 64, 400, 160, "center")
|
||||
self.cool_timer = self.cool_timer - 1
|
||||
end
|
||||
self:drawSectionTimesWithSecondary(current_section, self.sectionColourFunction)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
|
||||
|
||||
@@ -12,23 +12,16 @@ MarathonA1Game.hash = "MarathonA1"
|
||||
MarathonA1Game.tagline = "Can you score enough points to reach the title of Grand Master?"
|
||||
|
||||
|
||||
|
||||
function MarathonA1Game:new()
|
||||
MarathonA1Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.bravos = 0
|
||||
self.gm_conditions = {
|
||||
level300 = false,
|
||||
level500 = false,
|
||||
level999 = false
|
||||
}
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History4RollsRandomizer()
|
||||
|
||||
@@ -138,43 +131,41 @@ function MarathonA1Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if new_level == 999 then
|
||||
self.level = 999
|
||||
self.clear = true
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if self.grid:checkForBravo(cleared_lines) then
|
||||
self.bravo = 4
|
||||
self.bravos = self.bravos + 1
|
||||
else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if self.clear then return end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA1Game:checkGMRequirements(old_level, new_level)
|
||||
if old_level < 300 and new_level >= 300 then
|
||||
if self.score >= 12000 and self.frames <= frameTime(4,15) then
|
||||
if self.score > 12000 and self.frames <= sp(4,15) then
|
||||
self.gm_conditions["level300"] = true
|
||||
end
|
||||
elseif old_level < 500 and new_level >= 500 then
|
||||
if self.score >= 40000 and self.frames <= frameTime(7,30) then
|
||||
if self.score > 40000 and self.frames <= sp(7,30) then
|
||||
self.gm_conditions["level500"] = true
|
||||
end
|
||||
elseif old_level < 999 and new_level >= 999 then
|
||||
if self.score >= 126000 and self.frames <= frameTime(13,30) then
|
||||
self.gm_conditions["level999"] = true
|
||||
if self.score > 126000 and self.frames <= sp(13,30) then
|
||||
self.gm_conditions["level900"] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -194,23 +185,17 @@ function MarathonA1Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
|
||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||
@@ -218,11 +203,7 @@ function MarathonA1Game:drawScoringInfo()
|
||||
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end
|
||||
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@ MarathonA2Game.tagline = "The points don't matter! Can you reach the invisible r
|
||||
|
||||
function MarathonA2Game:new()
|
||||
MarathonA2Game.super:new()
|
||||
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
@@ -27,11 +27,7 @@ function MarathonA2Game:new()
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.enable_hold = false
|
||||
@@ -107,7 +103,6 @@ end
|
||||
function MarathonA2Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames < 0 then return false end
|
||||
if self.roll_frames > 3694 then
|
||||
self.completed = true
|
||||
if self.grade == 32 then
|
||||
@@ -126,33 +121,34 @@ function MarathonA2Game:onPieceEnter()
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
self:updateGrade(cleared_lines)
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
else self.lines = self.lines + cleared_lines end
|
||||
end
|
||||
|
||||
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||
self:updateSectionTimes(self.level, self.level + cleared_row_count)
|
||||
self.level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 and not self.clear then
|
||||
self.clear = true
|
||||
if self:qualifiesForMRoll() then
|
||||
self.grade = 32
|
||||
end
|
||||
self.grid:clear()
|
||||
if self:qualifiesForMRoll() then self.grade = 32 end
|
||||
self.roll_frames = -150
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
||||
if self.clear then return end
|
||||
if math.floor(old_level / 100) < math.floor(new_level / 100) or
|
||||
@@ -250,7 +246,7 @@ function MarathonA2Game:updateGrade(cleared_lines)
|
||||
end
|
||||
end
|
||||
|
||||
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 0 }
|
||||
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
|
||||
|
||||
function MarathonA2Game:qualifiesForMRoll()
|
||||
if not self.clear then return false end
|
||||
@@ -264,7 +260,7 @@ function MarathonA2Game:qualifiesForMRoll()
|
||||
local section_average = 0
|
||||
for section = 0, 4 do
|
||||
section_average = section_average + self.section_times[section]
|
||||
if self.section_times[section] > frameTime(1,05) then
|
||||
if self.section_times[section] > sp(1,05) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -277,7 +273,7 @@ function MarathonA2Game:qualifiesForMRoll()
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.grade < 31 or self.frames > frameTime(8,45) then
|
||||
if self.grade < 17 or self.frames > sp(8,45) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
@@ -329,35 +325,18 @@ function MarathonA2Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.clear then
|
||||
if self:qualifiesForMRoll() then
|
||||
if self.lines >= 32 and self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
else love.graphics.setColor(0, 1, 0, 1) end
|
||||
else
|
||||
if self.roll_frames > 3694 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
else love.graphics.setColor(0, 1, 0, 1) end
|
||||
end
|
||||
end
|
||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
|
||||
@@ -16,8 +16,8 @@ MarathonA3Game.tagline = "The game gets faster way more quickly! Can you get all
|
||||
|
||||
function MarathonA3Game:new()
|
||||
MarathonA3Game.super:new()
|
||||
|
||||
self.speed_level = 0
|
||||
|
||||
self.speed_level = 0
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.grade = 0
|
||||
@@ -29,25 +29,13 @@ function MarathonA3Game:new()
|
||||
self.section_start_time = 0
|
||||
self.section_70_times = { [0] = 0 }
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_cool = false
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 0
|
||||
|
||||
self.torikan_passed = false
|
||||
end
|
||||
|
||||
function MarathonA3Game:getARE()
|
||||
@@ -152,7 +140,6 @@ function MarathonA3Game:onPieceEnter()
|
||||
self:updateSectionTimes(self.level, self.level + 1)
|
||||
self.level = self.level + 1
|
||||
self.speed_level = self.speed_level + 1
|
||||
self.torikan_passed = self.level >= 500 and true or false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -170,20 +157,16 @@ function MarathonA3Game:onLineClear(cleared_row_count)
|
||||
self.grid:clear()
|
||||
self.roll_frames = -150
|
||||
end
|
||||
if not self.torikan_passed and self.level >= 500 and self.frames >= 25200 then
|
||||
self.level = 500
|
||||
self.game_over = true
|
||||
end
|
||||
end
|
||||
|
||||
local cool_cutoffs = {
|
||||
frameTime(0,52), frameTime(0,52), frameTime(0,49), frameTime(0,45), frameTime(0,45),
|
||||
frameTime(0,42), frameTime(0,42), frameTime(0,38), frameTime(0,38),
|
||||
sp(0,52), sp(0,52), sp(0,49), sp(0,45), sp(0,45),
|
||||
sp(0,42), sp(0,42), sp(0,38), sp(0,38),
|
||||
}
|
||||
|
||||
local regret_cutoffs = {
|
||||
frameTime(0,90), frameTime(0,75), frameTime(0,75), frameTime(0,68), frameTime(0,60),
|
||||
frameTime(0,60), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50),
|
||||
sp(0,90), sp(0,75), sp(0,75), sp(0,68), sp(0,60),
|
||||
sp(0,60), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
|
||||
}
|
||||
|
||||
function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
||||
@@ -196,53 +179,42 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
|
||||
self.speed_level = self.section_cool and self.speed_level + 100 or self.speed_level
|
||||
|
||||
if section_time > regret_cutoffs[section] then
|
||||
self.section_cool_grade = self.section_cool_grade - 1
|
||||
table.insert(self.section_status, "regret")
|
||||
self.coolregret_message = "REGRET!!"
|
||||
self.coolregret_timer = 300
|
||||
elseif self.section_cool then
|
||||
elseif section <= 9 and self.section_status[section - 1] == "cool" and
|
||||
self.section_70_times[section] < self.section_70_times[section - 1] + 120 then
|
||||
self.section_cool_grade = self.section_cool_grade + 1
|
||||
self.speed_level = self.speed_level + 100
|
||||
table.insert(self.section_status, "cool")
|
||||
elseif self.section_status[section - 1] == "cool" then
|
||||
table.insert(self.section_status, "none")
|
||||
elseif section <= 9 and self.section_70_times[section] < cool_cutoffs[section] then
|
||||
self.section_cool_grade = self.section_cool_grade + 1
|
||||
self.speed_level = self.speed_level + 100
|
||||
table.insert(self.section_status, "cool")
|
||||
else
|
||||
table.insert(self.section_status, "none")
|
||||
end
|
||||
|
||||
self.section_cool = false
|
||||
elseif old_level % 100 < 70 and new_level % 100 >= 70 then
|
||||
elseif section <= 9 and old_level % 100 < 70 and new_level % 100 >= 70 then
|
||||
-- record section 70 time
|
||||
section_70_time = self.frames - self.section_start_time
|
||||
table.insert(self.section_70_times, section_70_time)
|
||||
|
||||
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.section_cool = true
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 300
|
||||
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
|
||||
self.section_cool = true
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 300
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -400,16 +372,12 @@ function MarathonA3Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
-- draw section time data
|
||||
current_section = math.floor(self.level / 100) + 1
|
||||
@@ -436,25 +404,14 @@ function MarathonA3Game:drawScoringInfo()
|
||||
current_x = section_70_x
|
||||
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
|
||||
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||
self.coolregret_timer = self.coolregret_timer - 1
|
||||
end
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(self:getLetterGrade(), 240, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
169
tetris/modes/marathon_ax.lua
Normal file
169
tetris/modes/marathon_ax.lua
Normal file
@@ -0,0 +1,169 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local MarathonAXGame = GameMode:extend()
|
||||
|
||||
MarathonAXGame.name = "Marathon AX"
|
||||
MarathonAXGame.hash = "MarathonAX"
|
||||
MarathonAXGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function MarathonAXGame:new()
|
||||
MarathonAXGame.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function MarathonAXGame:getSectionTimeLimit()
|
||||
if self.lines < 20 then return 7200
|
||||
else return 5400 end
|
||||
end
|
||||
|
||||
function MarathonAXGame:getARE()
|
||||
return 27
|
||||
end
|
||||
|
||||
function MarathonAXGame:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function MarathonAXGame:getDasLimit()
|
||||
return 15
|
||||
end
|
||||
|
||||
function MarathonAXGame:getLineClearDelay()
|
||||
return 40
|
||||
end
|
||||
|
||||
function MarathonAXGame:getLockDelay()
|
||||
return 30
|
||||
end
|
||||
|
||||
function MarathonAXGame:getGravity()
|
||||
if self.lines < 10 then return 4/256
|
||||
elseif self.lines < 20 then return 12/256
|
||||
elseif self.lines < 30 then return 48/256
|
||||
elseif self.lines < 40 then return 72/256
|
||||
elseif self.lines < 50 then return 96/256
|
||||
elseif self.lines < 60 then return 1/2
|
||||
elseif self.lines < 70 then return 1
|
||||
elseif self.lines < 80 then return 3/2
|
||||
elseif self.lines < 90 then return 2
|
||||
elseif self.lines < 100 then return 3
|
||||
elseif self.lines < 110 then return 4
|
||||
elseif self.lines < 120 then return 5
|
||||
else return 20 end
|
||||
end
|
||||
|
||||
function MarathonAXGame:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames < 0 then
|
||||
return false
|
||||
elseif self.roll_frames > 2968 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
if not self.section_clear then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
if self:getSectionTime() >= self:getSectionTimeLimit() then
|
||||
self.game_over = true
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function MarathonAXGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_lines = self.lines + cleared_row_count
|
||||
self:updateSectionTimes(self.lines, new_lines)
|
||||
self.lines = math.min(new_lines, 150)
|
||||
if self.lines == 150 then
|
||||
self.clear = true
|
||||
self.roll_frames = -150
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAXGame:getSectionTime()
|
||||
return self.frames - self.section_start_time
|
||||
end
|
||||
|
||||
function MarathonAXGame:updateSectionTimes(old_lines, new_lines)
|
||||
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
|
||||
-- record new section
|
||||
table.insert(self.section_times, self:getSectionTime())
|
||||
self.section_start_time = self.frames
|
||||
self.section_clear = true
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAXGame:onPieceEnter()
|
||||
self.section_clear = false
|
||||
end
|
||||
|
||||
function MarathonAXGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
function MarathonAXGame:getHighscoreData()
|
||||
return {
|
||||
lines = self.lines,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function MarathonAXGame:drawScoringInfo()
|
||||
MarathonAXGame.super.drawScoringInfo(self)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||
love.graphics.printf("LINES", 240, 320, 40, "left")
|
||||
|
||||
local current_section = math.floor(self.lines / 10) + 1
|
||||
self:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.lines, 240, 340, 40, "right")
|
||||
love.graphics.printf(self.clear and self.lines or self:getSectionEndLines(), 240, 370, 40, "right")
|
||||
|
||||
-- draw time left, flash red if necessary
|
||||
local time_left = self:getSectionTimeLimit() - math.max(self:getSectionTime(), 0)
|
||||
if not self.game_over and not self.clear and time_left < sp(0,10) and time_left % 4 < 2 then
|
||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||
end
|
||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function MarathonAXGame:getSectionEndLines()
|
||||
return math.floor(self.lines / 10 + 1) * 10
|
||||
end
|
||||
|
||||
function MarathonAXGame:getBackground()
|
||||
return math.floor(self.lines / 10)
|
||||
end
|
||||
|
||||
return MarathonAXGame
|
||||
42
tetris/modes/marathon_ax2.lua
Normal file
42
tetris/modes/marathon_ax2.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
require 'funcs'
|
||||
|
||||
local MarathonAX = require 'tetris.modes.marathon_ax'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local MarathonAX2Game = MarathonAX:extend()
|
||||
|
||||
MarathonAX2Game.name = "Marathon AX2"
|
||||
MarathonAX2Game.hash = "MarathonAX2"
|
||||
MarathonAX2Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function MarathonAX2Game:new()
|
||||
MarathonAX2Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function MarathonAX2Game:getGravity()
|
||||
if self.lines < 10 then return 84/256
|
||||
elseif self.lines < 20 then return 1/2
|
||||
elseif self.lines < 30 then return 1
|
||||
elseif self.lines < 40 then return 2
|
||||
elseif self.lines < 50 then return 3
|
||||
elseif self.lines < 60 then return 4
|
||||
elseif self.lines < 70 then return 5
|
||||
else return 20 end
|
||||
end
|
||||
|
||||
return MarathonAX2Game
|
||||
35
tetris/modes/marathon_ax3.lua
Normal file
35
tetris/modes/marathon_ax3.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
require 'funcs'
|
||||
|
||||
local MarathonAX = require 'tetris.modes.marathon_ax'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local MarathonAX3Game = MarathonAX:extend()
|
||||
|
||||
MarathonAX3Game.name = "Marathon AX3"
|
||||
MarathonAX3Game.hash = "MarathonAX3"
|
||||
MarathonAX3Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function MarathonAX3Game:new()
|
||||
MarathonAX3Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function MarathonAX3Game:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
return MarathonAX3Game
|
||||
@@ -1,175 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart'
|
||||
|
||||
local MarathonAX4Game = GameMode:extend()
|
||||
|
||||
MarathonAX4Game.name = "Marathon AX4"
|
||||
MarathonAX4Game.hash = "MarathonAX4"
|
||||
MarathonAX4Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function MarathonAX4Game:new()
|
||||
MarathonAX4Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = Bag7NoSZOStartRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getARE()
|
||||
if self.lines < 10 then return 18
|
||||
elseif self.lines < 40 then return 14
|
||||
elseif self.lines < 60 then return 12
|
||||
elseif self.lines < 70 then return 10
|
||||
elseif self.lines < 80 then return 8
|
||||
elseif self.lines < 90 then return 7
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getDasLimit()
|
||||
if self.lines < 20 then return 10
|
||||
elseif self.lines < 50 then return 9
|
||||
elseif self.lines < 70 then return 8
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getLineClearDelay()
|
||||
if self.lines < 10 then return 14
|
||||
elseif self.lines < 30 then return 9
|
||||
else return 5 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getLockDelay()
|
||||
if self.lines < 10 then return 28
|
||||
elseif self.lines < 20 then return 24
|
||||
elseif self.lines < 30 then return 22
|
||||
elseif self.lines < 40 then return 20
|
||||
elseif self.lines < 50 then return 18
|
||||
elseif self.lines < 70 then return 14
|
||||
else return 13 end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSection()
|
||||
return math.floor(level / 100) + 1
|
||||
end
|
||||
|
||||
function MarathonAX4Game:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames < 0 then
|
||||
return false
|
||||
elseif self.roll_frames > 2968 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
if not self.section_clear then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
if self:getSectionTime() >= self.section_time_limit then
|
||||
self.game_over = true
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function MarathonAX4Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_lines = self.lines + cleared_row_count
|
||||
self:updateSectionTimes(self.lines, new_lines)
|
||||
self.lines = math.min(new_lines, 150)
|
||||
if self.lines == 150 then
|
||||
self.grid:clear()
|
||||
self.clear = true
|
||||
self.roll_frames = -150
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSectionTime()
|
||||
return self.frames - self.section_start_time
|
||||
end
|
||||
|
||||
function MarathonAX4Game:updateSectionTimes(old_lines, new_lines)
|
||||
if math.floor(old_lines / 10) < math.floor(new_lines / 10) then
|
||||
-- record new section
|
||||
table.insert(self.section_times, self:getSectionTime())
|
||||
self.section_start_time = self.frames
|
||||
self.section_clear = true
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonAX4Game:onPieceEnter()
|
||||
self.section_clear = false
|
||||
end
|
||||
|
||||
function MarathonAX4Game:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getHighscoreData()
|
||||
return {
|
||||
lines = self.lines,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function MarathonAX4Game:drawScoringInfo()
|
||||
MarathonAX4Game.super.drawScoringInfo(self)
|
||||
|
||||
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("TIME LEFT", 240, 250, 80, "left")
|
||||
love.graphics.printf("LINES", 240, 320, 40, "left")
|
||||
|
||||
local current_section = math.floor(self.lines / 10) + 1
|
||||
self:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.lines, 240, 340, 40, "right")
|
||||
love.graphics.printf(self.clear and self.lines or self:getSectionEndLines(), 240, 370, 40, "right")
|
||||
|
||||
-- draw time left, flash red if necessary
|
||||
local time_left = self.section_time_limit - math.max(self:getSectionTime(), 0)
|
||||
if not self.game_over and not self.clear and time_left < frameTime(0,10) and time_left % 4 < 2 then
|
||||
love.graphics.setColor(1, 0.3, 0.3, 1)
|
||||
end
|
||||
love.graphics.printf(formatTime(time_left), 240, 270, 160, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getSectionEndLines()
|
||||
return math.floor(self.lines / 10 + 1) * 10
|
||||
end
|
||||
|
||||
function MarathonAX4Game:getBackground()
|
||||
return math.floor(self.lines / 10)
|
||||
end
|
||||
|
||||
return MarathonAX4Game
|
||||
@@ -156,7 +156,7 @@ function MarathonC89Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("LINES", 240, 120, 40, "left")
|
||||
|
||||
133
tetris/modes/marathon_wcb.lua
Normal file
133
tetris/modes/marathon_wcb.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local MarathonWCBGame = GameMode:extend()
|
||||
|
||||
MarathonWCBGame.name = "Marathon WCB"
|
||||
MarathonWCBGame.hash = "MarathonWCB"
|
||||
MarathonWCBGame.tagline = "When all the pieces slip right to their destinations... can you keep up?"
|
||||
|
||||
|
||||
function MarathonWCBGame:new()
|
||||
MarathonWCBGame.super:new()
|
||||
|
||||
self.pieces = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.instant_hard_drop = true
|
||||
self.instant_soft_drop = true
|
||||
self.enable_hold = false
|
||||
self.next_queue_length = 3
|
||||
|
||||
self.piece_is_active = false
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getDropSpeed()
|
||||
return 20
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getARR()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getARE()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getLineARE()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getDasLimit()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getLineClearDelay()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getLockDelay()
|
||||
return math.huge
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getGravity()
|
||||
return self.piece_is_active and 20 or 0
|
||||
end
|
||||
|
||||
function MarathonWCBGame:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames > 150 then
|
||||
self.completed = true
|
||||
end
|
||||
return false
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function MarathonWCBGame:onAttemptPieceMove()
|
||||
if self.piece ~= nil then
|
||||
-- don't let the piece move before it's finished dropping
|
||||
self.piece:dropToBottom(self.grid)
|
||||
end
|
||||
self.piece_is_active = true
|
||||
end
|
||||
|
||||
function MarathonWCBGame:onAttemptPieceRotate()
|
||||
self.piece_is_active = true
|
||||
end
|
||||
|
||||
function MarathonWCBGame:onPieceLock()
|
||||
self.piece_is_active = false
|
||||
self.pieces = self.pieces + 1
|
||||
end
|
||||
|
||||
function MarathonWCBGame:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
self.lines = self.lines + cleared_row_count
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonWCBGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getHighscoreData()
|
||||
return {
|
||||
pieces = self.pieces,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function MarathonWCBGame:drawScoringInfo()
|
||||
MarathonWCBGame.super.drawScoringInfo(self)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
local text_x = config["side_next"] and 320 or 240
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("lines", text_x, 160, 80, "left")
|
||||
love.graphics.printf("pieces", text_x, 220, 80, "left")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.lines, text_x, 180, 80, "left")
|
||||
love.graphics.printf(self.pieces, text_x, 240, 80, "left")
|
||||
end
|
||||
|
||||
function MarathonWCBGame:getBackground()
|
||||
return (math.floor(self.pieces / 50) % 20)
|
||||
end
|
||||
|
||||
return MarathonWCBGame
|
||||
@@ -50,6 +50,9 @@ function PacerTest:new()
|
||||
end
|
||||
|
||||
function PacerTest:initialize(ruleset)
|
||||
for i = 1, 30 do
|
||||
table.insert(self.next_queue, self:getNextPiece(ruleset))
|
||||
end
|
||||
self.level_frames = getLevelFrames(1)
|
||||
switchBGM("pacer_test")
|
||||
end
|
||||
@@ -16,12 +16,6 @@ function PhantomManiaGame:new()
|
||||
|
||||
self.lock_drop = true
|
||||
self.next_queue_length = 1
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
@@ -67,15 +61,15 @@ function PhantomManiaGame:getGravity()
|
||||
end
|
||||
|
||||
function PhantomManiaGame:hitTorikan(old_level, new_level)
|
||||
if old_level < 300 and new_level >= 300 and self.frames > frameTime(2,28) then
|
||||
if old_level < 300 and new_level >= 300 and self.frames > sp(2,28) then
|
||||
self.level = 300
|
||||
return true
|
||||
end
|
||||
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,38) then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,38) then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
if old_level < 800 and new_level >= 800 and self.frames > frameTime(5,23) then
|
||||
if old_level < 800 and new_level >= 800 and self.frames > sp(5,23) then
|
||||
self.level = 800
|
||||
return true
|
||||
end
|
||||
@@ -117,17 +111,16 @@ function PhantomManiaGame:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function PhantomManiaGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -168,16 +161,12 @@ function PhantomManiaGame:drawScoringInfo()
|
||||
local text_x = config["side_next"] and 320 or 240
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end
|
||||
love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
if self.clear then
|
||||
@@ -186,9 +175,6 @@ function PhantomManiaGame:drawScoringInfo()
|
||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
||||
end
|
||||
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
end
|
||||
|
||||
function PhantomManiaGame:getSectionEndLevel()
|
||||
|
||||
@@ -16,6 +16,7 @@ PhantomMania2Game.tagline = "The blocks disappear even faster now! Can you make
|
||||
|
||||
function PhantomMania2Game:new()
|
||||
PhantomMania2Game.super:new()
|
||||
self.level = 0
|
||||
self.grade = 0
|
||||
self.garbage = 0
|
||||
self.clear = false
|
||||
@@ -25,21 +26,12 @@ function PhantomMania2Game:new()
|
||||
self.hold_age = 0
|
||||
self.queue_age = 0
|
||||
self.roll_points = 0
|
||||
|
||||
self.SGnames = {
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
|
||||
self.coolregret_message = ""
|
||||
self.coolregret_timer = 0
|
||||
end
|
||||
|
||||
function PhantomMania2Game:getARE()
|
||||
@@ -94,19 +86,19 @@ function PhantomMania2Game:getNextPiece(ruleset)
|
||||
end
|
||||
|
||||
function PhantomMania2Game:hitTorikan(old_level, new_level)
|
||||
if old_level < 300 and new_level >= 300 and self.frames > frameTime(2,02) then
|
||||
if old_level < 300 and new_level >= 300 and self.frames > sp(2,02) then
|
||||
self.level = 300
|
||||
return true
|
||||
end
|
||||
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,03) then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,03) then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
if old_level < 800 and new_level >= 800 and self.frames > frameTime(4,45) then
|
||||
if old_level < 800 and new_level >= 800 and self.frames > sp(4,45) then
|
||||
self.level = 800
|
||||
return true
|
||||
end
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(5,38) then
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,38) then
|
||||
self.level = 1000
|
||||
return true
|
||||
end
|
||||
@@ -164,7 +156,7 @@ function PhantomMania2Game:onLineClear(cleared_row_count)
|
||||
end
|
||||
self:advanceBottomRow(-cleared_row_count)
|
||||
else
|
||||
self.roll_points = self.roll_points + cleared_row_points[cleared_row_count / 2]
|
||||
self.roll_points = self.roll_points + cleared_row_points[cleared_row_count]
|
||||
if self.roll_points >= 100 then
|
||||
self.roll_points = self.roll_points - 100
|
||||
self.grade = self.grade + 1
|
||||
@@ -181,31 +173,30 @@ function PhantomMania2Game:onHold()
|
||||
end
|
||||
|
||||
function PhantomMania2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local cool_cutoffs = {
|
||||
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,27), frameTime(0,27), frameTime(0,27),
|
||||
sp(0,36), sp(0,36), sp(0,36), sp(0,36), sp(0,36),
|
||||
sp(0,30), sp(0,30), sp(0,30), sp(0,30), sp(0,30),
|
||||
sp(0,27), sp(0,27), sp(0,27),
|
||||
}
|
||||
|
||||
local regret_cutoffs = {
|
||||
frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50), frameTime(0,50),
|
||||
frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40), frameTime(0,40),
|
||||
frameTime(0,35), frameTime(0,35), frameTime(0,35),
|
||||
sp(0,50), sp(0,50), sp(0,50), sp(0,50), sp(0,50),
|
||||
sp(0,40), sp(0,40), sp(0,40), sp(0,40), sp(0,40),
|
||||
sp(0,35), sp(0,35), sp(0,35),
|
||||
}
|
||||
|
||||
function PhantomMania2Game:updateSectionTimes(old_level, new_level)
|
||||
@@ -216,13 +207,8 @@ function PhantomMania2Game:updateSectionTimes(old_level, new_level)
|
||||
self.section_start_time = self.frames
|
||||
if section_time <= cool_cutoffs[section] then
|
||||
self.grade = self.grade + 2
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 300
|
||||
elseif section_time <= regret_cutoffs[section] then
|
||||
self.grade = self.grade + 1
|
||||
else
|
||||
self.coolregret_message = "REGRET!!"
|
||||
self.coolregret_timer = 300
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -248,7 +234,7 @@ PhantomMania2Game.garbageOpacityFunction = function(age)
|
||||
end
|
||||
|
||||
function PhantomMania2Game:drawGrid()
|
||||
if not (self.game_over or (self.clear and self.level < 1300)) then
|
||||
if not (self.game_over) then
|
||||
self.grid:drawInvisible(self.rollOpacityFunction, self.garbageOpacityFunction)
|
||||
else
|
||||
self.grid:draw()
|
||||
@@ -299,15 +285,6 @@ function PhantomMania2Game:drawScoringInfo()
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
if(self.coolregret_timer > 0) then
|
||||
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||
self.coolregret_timer = self.coolregret_timer - 1
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
||||
@@ -318,10 +295,6 @@ function PhantomMania2Game:drawScoringInfo()
|
||||
else
|
||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||
end
|
||||
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
end
|
||||
|
||||
function PhantomMania2Game:getBackground()
|
||||
|
||||
@@ -8,12 +8,6 @@ PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS su
|
||||
|
||||
function PhantomManiaNGame:new()
|
||||
PhantomManiaNGame.super:new()
|
||||
|
||||
self.SGnames = {
|
||||
"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
|
||||
"M10", "M11", "M12", "M13", "M14", "M15", "M16", "M17", "M18",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.next_queue_length = 3
|
||||
self.enable_hold = true
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'funcs'
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local Race40Game = GameMode:extend()
|
||||
|
||||
@@ -18,22 +18,14 @@ function Race40Game:new()
|
||||
self.lines = 0
|
||||
self.line_goal = 40
|
||||
self.pieces = 0
|
||||
self.randomizer = Bag7Randomiser()
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.roll_frames = 0
|
||||
|
||||
self.SGnames = {
|
||||
[0] = "",
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
self.upstacked = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.instant_hard_drop = true
|
||||
self.instant_soft_drop = false
|
||||
self.instant_soft_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
@@ -43,23 +35,23 @@ function Race40Game:getDropSpeed()
|
||||
end
|
||||
|
||||
function Race40Game:getARR()
|
||||
return 1
|
||||
return 0
|
||||
end
|
||||
|
||||
function Race40Game:getARE()
|
||||
return 0
|
||||
return 4
|
||||
end
|
||||
|
||||
function Race40Game:getLineARE()
|
||||
return self:getARE()
|
||||
return 2
|
||||
end
|
||||
|
||||
function Race40Game:getDasLimit()
|
||||
return 10
|
||||
return 6
|
||||
end
|
||||
|
||||
function Race40Game:getLineClearDelay()
|
||||
return 0
|
||||
return 2
|
||||
end
|
||||
|
||||
function Race40Game:getLockDelay()
|
||||
@@ -67,7 +59,7 @@ function Race40Game:getLockDelay()
|
||||
end
|
||||
|
||||
function Race40Game:getGravity()
|
||||
return 1/64
|
||||
return 20
|
||||
end
|
||||
|
||||
function Race40Game:advanceOneFrame()
|
||||
@@ -110,12 +102,6 @@ function Race40Game:getHighscoreData()
|
||||
}
|
||||
end
|
||||
|
||||
function Race40Game:getSecretGrade(sg)
|
||||
if sg == 19 then self.upstacked = true end
|
||||
if self.upstacked then return self.SGnames[14 + math.floor((20 - sg) / 4)]
|
||||
else return self.SGnames[math.floor((sg / 19) * 14)] end
|
||||
end
|
||||
|
||||
function Race40Game:drawScoringInfo()
|
||||
Race40Game.super.drawScoringInfo(self)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
@@ -127,17 +113,10 @@ function Race40Game:drawScoringInfo()
|
||||
love.graphics.printf("LINES", text_x, 320, 40, "left")
|
||||
love.graphics.printf("line/min", text_x, 160, 80, "left")
|
||||
love.graphics.printf("piece/sec", text_x, 220, 80, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 7 or self.upstacked then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(string.format("%.02f", self.lines / math.max(1, self.frames) * 3600), text_x, 180, 80, "left")
|
||||
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.frames) * 60), text_x, 240, 80, "left")
|
||||
if sg >= 7 or self.upstacked then
|
||||
love.graphics.printf(self:getSecretGrade(sg), 240, 450, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_4)
|
||||
love.graphics.printf(math.max(0, self.line_goal - self.lines), text_x, 340, 40, "left")
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
|
||||
|
||||
local ScoreDrainGame = GameMode:extend()
|
||||
|
||||
ScoreDrainGame.name = "Score Drain"
|
||||
ScoreDrainGame.hash = "ScoreDrain"
|
||||
ScoreDrainGame.tagline = "Your score goes down over time! Avoid hitting 0 points, or your game is over!"
|
||||
|
||||
function ScoreDrainGame:new()
|
||||
self.super:new()
|
||||
|
||||
self.score = 2500
|
||||
self.drain_rate = 50
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = true
|
||||
self.lock_hard_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getARE()
|
||||
if self.level < 700 then return 27
|
||||
elseif self.level < 800 then return 18
|
||||
elseif self.level < 1000 then return 14
|
||||
elseif self.level < 1100 then return 8
|
||||
elseif self.level < 1200 then return 7
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getLineARE()
|
||||
if self.level < 600 then return 27
|
||||
elseif self.level < 700 then return 18
|
||||
elseif self.level < 800 then return 14
|
||||
elseif self.level < 1100 then return 8
|
||||
elseif self.level < 1200 then return 7
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getDasLimit()
|
||||
if self.level < 500 then return 15
|
||||
elseif self.level < 900 then return 9
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getLineClearDelay()
|
||||
if self.level < 500 then return 40
|
||||
elseif self.level < 600 then return 25
|
||||
elseif self.level < 700 then return 16
|
||||
elseif self.level < 800 then return 12
|
||||
elseif self.level < 1100 then return 6
|
||||
elseif self.level < 1200 then return 5
|
||||
else return 4 end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getLockDelay()
|
||||
if self.level < 900 then return 30
|
||||
elseif self.level < 1100 then return 17
|
||||
else return 15 end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getGravity()
|
||||
if (self.level < 30) then return 4/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 < 120) then return 80/256
|
||||
elseif (self.level < 140) then return 96/256
|
||||
elseif (self.level < 160) then return 112/256
|
||||
elseif (self.level < 170) then return 128/256
|
||||
elseif (self.level < 200) then return 144/256
|
||||
elseif (self.level < 220) then return 4/256
|
||||
elseif (self.level < 230) then return 32/256
|
||||
elseif (self.level < 233) then return 64/256
|
||||
elseif (self.level < 236) then return 96/256
|
||||
elseif (self.level < 239) then return 128/256
|
||||
elseif (self.level < 243) then return 160/256
|
||||
elseif (self.level < 247) then return 192/256
|
||||
elseif (self.level < 251) then return 224/256
|
||||
elseif (self.level < 300) then return 1
|
||||
elseif (self.level < 330) then return 2
|
||||
elseif (self.level < 360) then return 3
|
||||
elseif (self.level < 400) then return 4
|
||||
elseif (self.level < 420) then return 5
|
||||
elseif (self.level < 450) then return 4
|
||||
elseif (self.level < 500) then return 3
|
||||
else return 20
|
||||
end
|
||||
end
|
||||
|
||||
function ScoreDrainGame:advanceOneFrame()
|
||||
if self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
self.score = math.max(0, self.score - self.drain_rate / 60)
|
||||
self.game_over = self.score <= 0 and true or false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ScoreDrainGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99) and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
local cleared_row_levels = {1, 2, 4, 6}
|
||||
|
||||
function ScoreDrainGame:onLineClear(cleared_row_count)
|
||||
local new_level = self.level + cleared_row_levels[cleared_row_count]
|
||||
self.drain_rate = math.floor(self.level / 100) < math.floor(new_level / 100) and self.drain_rate * 1.5 or self.drain_rate
|
||||
self.level = new_level
|
||||
end
|
||||
|
||||
function ScoreDrainGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
|
||||
function ScoreDrainGame:drawGrid()
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil and self.level < 100 then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
function ScoreDrainGame: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("DRAIN RATE", 240, 90, 80, "left")
|
||||
love.graphics.printf("SCORE", 240, 170, 40, "left")
|
||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(math.floor(self.drain_rate).."/s", 240, 110, 120, "left")
|
||||
local frames_left = self.score / self.drain_rate * 60
|
||||
if frames_left <= 600 and frames_left % 4 < 2 and not self.game_over then love.graphics.setColor(1, 0.3, 0.3, 1) end
|
||||
love.graphics.printf(formatTime(frames_left), 240, 270, 120, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(math.floor(self.score), 240, 190, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 50, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 50, "right")
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getHighscoreData()
|
||||
return {
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getSectionEndLevel()
|
||||
return math.floor(self.level / 100 + 1) * 100
|
||||
end
|
||||
|
||||
function ScoreDrainGame:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
return ScoreDrainGame
|
||||
@@ -16,6 +16,7 @@ StrategyGame.tagline = "You have lots of time to think! Can you use it to place
|
||||
|
||||
function StrategyGame:new()
|
||||
StrategyGame.super:new()
|
||||
self.level = 0
|
||||
self.clear = false
|
||||
self.completed = false
|
||||
self.roll_frames = 0
|
||||
@@ -83,7 +84,7 @@ function StrategyGame:advanceOneFrame()
|
||||
end
|
||||
|
||||
function StrategyGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
@@ -98,17 +99,16 @@ function StrategyGame:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function StrategyGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -135,11 +135,11 @@ function StrategyGame:drawScoringInfo()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
||||
if self.clear then
|
||||
love.graphics.printf(self.level, text_x, 370, 40, "right")
|
||||
love.graphics.printf(self.level, text_x, 370, 50, "right")
|
||||
else
|
||||
love.graphics.printf(self.level < 900 and math.floor(self.level / 100 + 1) * 100 or 999, text_x, 370, 40, "right")
|
||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -102,15 +102,15 @@ function Survival2020Game:getNextPiece(ruleset)
|
||||
end
|
||||
|
||||
function Survival2020Game:hitTorikan(old_level, new_level)
|
||||
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,00) then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,00) then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(5,00) then
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(5,00) then
|
||||
self.level = 1000
|
||||
return true
|
||||
end
|
||||
if old_level < 1500 and new_level >= 1500 and self.frames > frameTime(7,00) then
|
||||
if old_level < 1500 and new_level >= 1500 and self.frames > sp(7,00) then
|
||||
self.level = 1500
|
||||
return true
|
||||
end
|
||||
@@ -174,17 +174,16 @@ function Survival2020Game:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function Survival2020Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -194,7 +193,7 @@ function Survival2020Game:updateSectionTimes(old_level, new_level)
|
||||
section_time = self.frames - self.section_start_time
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
if section_time <= frameTime(0,30) then
|
||||
if section_time <= sp(0,30) then
|
||||
self.grade = self.grade + 2
|
||||
else
|
||||
self.grade = self.grade + 1
|
||||
|
||||
@@ -19,20 +19,12 @@ function SurvivalA1Game:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.bravos = 0
|
||||
|
||||
self.gm_conditions = {
|
||||
level300 = false,
|
||||
level500 = false,
|
||||
level999 = false
|
||||
}
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History4RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
@@ -109,45 +101,40 @@ function SurvivalA1Game:onLineClear(cleared_row_count)
|
||||
self:checkGMRequirements(self.level, self.level + cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if new_level == 999 then
|
||||
if self.level == 999 then
|
||||
self.clear = true
|
||||
else
|
||||
self.level = new_level
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if self.grid:checkForBravo(cleared_lines) then
|
||||
self.bravo = 4
|
||||
self.bravos = self.bravos + 1
|
||||
else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * self.combo
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA1Game:checkGMRequirements(old_level, new_level)
|
||||
if old_level < 300 and new_level >= 300 then
|
||||
if self.score >= 12000 and self.frames <= frameTime(4,15) then
|
||||
if self.score > 12000 and self.frames <= sp(4,15) then
|
||||
self.gm_conditions["level300"] = true
|
||||
end
|
||||
elseif old_level < 500 and new_level >= 500 then
|
||||
if self.score >= 40000 and self.frames <= frameTime(7,30) then
|
||||
if self.score > 40000 and self.frames <= sp(7,30) then
|
||||
self.gm_conditions["level500"] = true
|
||||
end
|
||||
elseif old_level < 999 and new_level >= 999 then
|
||||
if self.score >= 126000 and self.frames <= frameTime(13,30) then
|
||||
self.gm_conditions["level999"] = true
|
||||
if self.score > 126000 and self.frames <= sp(13,30) then
|
||||
self.gm_conditions["level900"] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -164,23 +151,17 @@ function SurvivalA1Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("NEXT RANK", 240, 260, 90, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
if self.bravos > 0 then love.graphics.printf("BRAVO", 300, 120, 40, "left") end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level900"] then
|
||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||
@@ -188,10 +169,6 @@ function SurvivalA1Game:drawScoringInfo()
|
||||
love.graphics.printf(getRankForScore(self.score).next, 240, 280, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
if self.bravos > 0 then love.graphics.printf(self.bravos, 300, 140, 40, "left") end
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
|
||||
@@ -19,12 +19,6 @@ function SurvivalA2Game:new()
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.lock_drop = true
|
||||
end
|
||||
@@ -68,7 +62,7 @@ function SurvivalA2Game:getGravity()
|
||||
end
|
||||
|
||||
function SurvivalA2Game:hitTorikan(old_level, new_level)
|
||||
if old_level < 500 and new_level >= 500 and self.frames > frameTime(3,25) then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > sp(3,25) then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
@@ -88,7 +82,7 @@ function SurvivalA2Game:advanceOneFrame()
|
||||
end
|
||||
|
||||
function SurvivalA2Game:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
@@ -98,9 +92,6 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
|
||||
local new_level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 or self:hitTorikan(self.level, new_level) then
|
||||
self.clear = true
|
||||
if self.level < 999 then
|
||||
self.game_over = true
|
||||
end
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
@@ -108,25 +99,22 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function SurvivalA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * self.combo
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA2Game:getLetterGrade()
|
||||
if self.level >= 999 then return "GM"
|
||||
elseif self.level > 500 then return "M"
|
||||
elseif self.level == 500 and not self.clear then return "M"
|
||||
elseif self.level >= 500 then return "M"
|
||||
else return "" end
|
||||
end
|
||||
|
||||
@@ -144,27 +132,18 @@ function SurvivalA2Game:drawScoringInfo()
|
||||
love.graphics.print(
|
||||
self.das.direction .. " " ..
|
||||
self.das.frames .. " " ..
|
||||
strTrueValues(self.prev_inputs)
|
||||
st(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
if self:getLetterGrade() ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
|
||||
love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA2Game:getSectionEndLevel()
|
||||
|
||||
@@ -16,6 +16,7 @@ SurvivalA3Game.tagline = "The blocks turn black and white! Can you make it to le
|
||||
|
||||
function SurvivalA3Game:new()
|
||||
SurvivalA3Game.super:new()
|
||||
self.level = 0
|
||||
self.grade = 0
|
||||
self.garbage = 0
|
||||
self.clear = false
|
||||
@@ -23,27 +24,10 @@ function SurvivalA3Game:new()
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.SGnames = {
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 0
|
||||
end
|
||||
|
||||
function SurvivalA3Game:initialize(ruleset)
|
||||
|
||||
self.torikan_time = frameTime(2,28)
|
||||
if ruleset.world then self.torikan_time = frameTime(3,03) end
|
||||
self.super.initialize(self, ruleset)
|
||||
-- ^ notice the . here instead of the :
|
||||
end
|
||||
|
||||
function SurvivalA3Game:getARE()
|
||||
@@ -102,11 +86,11 @@ function SurvivalA3Game:getNextPiece(ruleset)
|
||||
end
|
||||
|
||||
function SurvivalA3Game:hitTorikan(old_level, new_level)
|
||||
if old_level < 500 and new_level >= 500 and self.frames > self.torikan_time then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > sp(2,28) then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > self.torikan_time*2 then
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > sp(4,56) then
|
||||
self.level = 1000
|
||||
return true
|
||||
end
|
||||
@@ -139,21 +123,20 @@ function SurvivalA3Game:onPieceEnter()
|
||||
end
|
||||
|
||||
local cleared_row_levels = {1, 2, 4, 6}
|
||||
local cleared_row_points = {0.02, 0.05, 0.15, 0.6}
|
||||
|
||||
function SurvivalA3Game:onLineClear(cleared_row_count)
|
||||
if not self.clear then
|
||||
local new_level = self.level + cleared_row_levels[cleared_row_count]
|
||||
self:updateSectionTimes(self.level, new_level)
|
||||
if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
|
||||
self.clear = true
|
||||
if new_level >= 1300 then
|
||||
self.level = 1300
|
||||
self.grid:clear()
|
||||
self.big_mode = true
|
||||
self.roll_frames = -150
|
||||
else
|
||||
self.game_over = true
|
||||
self.big_mode = true
|
||||
end
|
||||
self.clear = true
|
||||
self.grid:clear()
|
||||
self.roll_frames = -150
|
||||
else
|
||||
self.level = math.min(new_level, 1300)
|
||||
end
|
||||
@@ -166,17 +149,16 @@ function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
|
||||
end
|
||||
|
||||
function SurvivalA3Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
if cleared_lines > 0 then
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * (cleared_lines * 2 - 1) * (self.combo * 2 - 1)
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + cleared_lines - 1
|
||||
else
|
||||
self.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -186,11 +168,8 @@ function SurvivalA3Game:updateSectionTimes(old_level, new_level)
|
||||
section_time = self.frames - self.section_start_time
|
||||
table.insert(self.section_times, section_time)
|
||||
self.section_start_time = self.frames
|
||||
if section_time <= frameTime(1,00) then
|
||||
if section_time <= sp(1,00) then
|
||||
self.grade = self.grade + 1
|
||||
else
|
||||
self.coolregret_message = "REGRET!!"
|
||||
self.coolregret_timer = 300
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -228,24 +207,12 @@ function SurvivalA3Game:drawScoringInfo()
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
if(self.coolregret_timer > 0) then
|
||||
love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
|
||||
self.coolregret_timer = self.coolregret_timer - 1
|
||||
end
|
||||
|
||||
local current_section = math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
|
||||
elseif self.clear then love.graphics.setColor(0, 1, 0, 1) end
|
||||
love.graphics.printf(getLetterGrade(math.floor(self.grade)), text_x, 140, 90, "left")
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 50, "right")
|
||||
if self.clear then
|
||||
@@ -253,9 +220,6 @@ function SurvivalA3Game:drawScoringInfo()
|
||||
else
|
||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||
end
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
end
|
||||
|
||||
function SurvivalA3Game:getBackground()
|
||||
|
||||
76
tetris/modes/survival_ax.lua
Normal file
76
tetris/modes/survival_ax.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
require 'funcs'
|
||||
|
||||
local MarathonAX = require 'tetris.modes.marathon_ax'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local SurvivalAXGame = MarathonAX:extend()
|
||||
|
||||
SurvivalAXGame.name = "Survival AX"
|
||||
SurvivalAXGame.hash = "SurvivalAX"
|
||||
SurvivalAXGame.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function SurvivalAXGame:new()
|
||||
SurvivalAXGame.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getSectionTimeLimit()
|
||||
return 3600
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getARE()
|
||||
if self.lines < 10 then return 18
|
||||
elseif self.lines < 40 then return 14
|
||||
elseif self.lines < 60 then return 12
|
||||
elseif self.lines < 70 then return 10
|
||||
elseif self.lines < 80 then return 8
|
||||
elseif self.lines < 90 then return 7
|
||||
else return 6 end
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getDasLimit()
|
||||
if self.lines < 20 then return 10
|
||||
elseif self.lines < 50 then return 9
|
||||
elseif self.lines < 70 then return 8
|
||||
else return 7 end
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getLineClearDelay()
|
||||
if self.lines < 10 then return 14
|
||||
elseif self.lines < 30 then return 8
|
||||
else return 5 end
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getLockDelay()
|
||||
if self.lines < 10 then return 30
|
||||
elseif self.lines < 20 then return 26
|
||||
elseif self.lines < 30 then return 24
|
||||
elseif self.lines < 40 then return 22
|
||||
elseif self.lines < 50 then return 20
|
||||
elseif self.lines < 70 then return 16
|
||||
else return 15 end
|
||||
end
|
||||
|
||||
function SurvivalAXGame:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
return SurvivalAXGame
|
||||
59
tetris/modes/survival_ax2.lua
Normal file
59
tetris/modes/survival_ax2.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
require 'funcs'
|
||||
|
||||
local MarathonAX2 = require 'tetris.modes.marathon_ax2'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local SurvivalAX2Game = MarathonAX2:extend()
|
||||
|
||||
SurvivalAX2Game.name = "Survival AX2"
|
||||
SurvivalAX2Game.hash = "SurvivalAX2"
|
||||
SurvivalAX2Game.tagline = "Can you clear the time hurdles when the game goes this fast?"
|
||||
|
||||
|
||||
function SurvivalAX2Game:new()
|
||||
SurvivalAX2Game.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.section_time_limit = 3600
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_clear = false
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getSectionTimeLimit()
|
||||
return 3600
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getARE()
|
||||
return 6
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getLineARE()
|
||||
return self:getARE()
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getDasLimit()
|
||||
return 7
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getLineClearDelay()
|
||||
return 5
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getLockDelay()
|
||||
return 15
|
||||
end
|
||||
|
||||
function SurvivalAX2Game:getGravity()
|
||||
return 20
|
||||
end
|
||||
|
||||
return SurvivalAX2Game
|
||||
@@ -1,209 +0,0 @@
|
||||
require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
|
||||
local TGMPlusGame = GameMode:extend()
|
||||
|
||||
TGMPlusGame.name = "Marathon A2+"
|
||||
TGMPlusGame.hash = "A2Plus"
|
||||
TGMPlusGame.tagline = "The garbage rises steadily! Can you make it to level 999?"
|
||||
|
||||
function TGMPlusGame:new()
|
||||
TGMPlusGame.super:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.enable_hold = false
|
||||
self.next_queue_length = 1
|
||||
|
||||
self.garbage_queue = 0
|
||||
self.garbage_pos = 0
|
||||
self.garbage_rows = {
|
||||
[0] = {"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"e", "e", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"e", "b", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "e", "e"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "b", "e"},
|
||||
{"b", "b", "e", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"b", "e", "e", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"b", "e", "b", "b", "b", "b", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "e", "b", "b"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "e", "e", "b"},
|
||||
{"b", "b", "b", "b", "b", "b", "b", "b", "e", "b"},
|
||||
{"b", "b", "b", "b", "e", "e", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "b", "e", "e", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "b", "e", "b", "b", "b", "b", "b"},
|
||||
{"b", "b", "b", "e", "e", "e", "b", "b", "b", "b"},
|
||||
}
|
||||
end
|
||||
|
||||
function TGMPlusGame:getARE() return 25 end
|
||||
function TGMPlusGame:getDasLimit() return 15 end
|
||||
function TGMPlusGame:getLockDelay() return 30 end
|
||||
function TGMPlusGame:getLineClearDelay() return 40 end
|
||||
|
||||
function TGMPlusGame:getGravity()
|
||||
if (self.level < 30) then return 4/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 < 120) then return 80/256
|
||||
elseif (self.level < 140) then return 96/256
|
||||
elseif (self.level < 160) then return 112/256
|
||||
elseif (self.level < 170) then return 128/256
|
||||
elseif (self.level < 200) then return 144/256
|
||||
elseif (self.level < 220) then return 4/256
|
||||
elseif (self.level < 230) then return 32/256
|
||||
elseif (self.level < 233) then return 64/256
|
||||
elseif (self.level < 236) then return 96/256
|
||||
elseif (self.level < 239) then return 128/256
|
||||
elseif (self.level < 243) then return 160/256
|
||||
elseif (self.level < 247) then return 192/256
|
||||
elseif (self.level < 251) then return 224/256
|
||||
elseif (self.level < 300) then return 1
|
||||
elseif (self.level < 330) then return 2
|
||||
elseif (self.level < 360) then return 3
|
||||
elseif (self.level < 400) then return 4
|
||||
elseif (self.level < 420) then return 5
|
||||
elseif (self.level < 450) then return 4
|
||||
elseif (self.level < 500) then return 3
|
||||
else return 20
|
||||
end
|
||||
end
|
||||
|
||||
function TGMPlusGame:getGarbageLimit() return 13 - math.floor(self.level / 100) end
|
||||
|
||||
function TGMPlusGame:advanceOneFrame()
|
||||
if self.clear then
|
||||
self.roll_frames = self.roll_frames + 1
|
||||
if self.roll_frames > 3694 then
|
||||
self.completed = true
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function TGMPlusGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 and self.level ~= 998) and not self.clear and self.frames ~= 0 then
|
||||
self.level = self.level + 1
|
||||
end
|
||||
end
|
||||
|
||||
function TGMPlusGame:onPieceLock(piece, cleared_row_count)
|
||||
if cleared_row_count == 0 then self:advanceBottomRow() end
|
||||
end
|
||||
|
||||
function TGMPlusGame:onLineClear(cleared_row_count)
|
||||
self.level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 and not self.clear then self.clear = true end
|
||||
end
|
||||
|
||||
function TGMPlusGame:advanceBottomRow()
|
||||
self.garbage_queue = self.garbage_queue + 1
|
||||
if self.garbage_queue >= self:getGarbageLimit() then
|
||||
self.grid:garbageRise(self.garbage_rows[self.garbage_pos])
|
||||
self.garbage_queue = 0
|
||||
self.garbage_pos = (self.garbage_pos + 1) % 24
|
||||
end
|
||||
end
|
||||
|
||||
function TGMPlusGame:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
self.score = self.score + (
|
||||
(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
|
||||
cleared_lines * self.combo * self.bravo
|
||||
)
|
||||
else
|
||||
self.combo = 1
|
||||
end
|
||||
self.drop_bonus = 0
|
||||
end
|
||||
end
|
||||
|
||||
function TGMPlusGame:drawGrid(ruleset)
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil and self.level < 100 then
|
||||
self:drawGhostPiece(ruleset)
|
||||
end
|
||||
end
|
||||
|
||||
function TGMPlusGame:getHighscoreData()
|
||||
return {
|
||||
score = self.score,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
function TGMPlusGame:getSectionEndLevel()
|
||||
if self.level >= 900 then return 999
|
||||
else return math.floor(self.level / 100 + 1) * 100 end
|
||||
end
|
||||
|
||||
function TGMPlusGame:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
end
|
||||
|
||||
function TGMPlusGame: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("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
if sg >= 5 then
|
||||
love.graphics.printf("SECRET GRADE", 240, 430, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, 240, 220, 90, "left")
|
||||
love.graphics.printf(self.level, 240, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
love.graphics.printf(self.SGnames[sg], 240, 450, 180, "left")
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_8x11)
|
||||
love.graphics.printf(formatTime(self.frames), 64, 420, 160, "center")
|
||||
end
|
||||
|
||||
|
||||
return TGMPlusGame
|
||||
@@ -2,8 +2,17 @@ local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local AlwaysRandomizer = Randomizer:extend()
|
||||
|
||||
function AlwaysRandomizer:new(piece)
|
||||
self.piece = piece
|
||||
self:initialize()
|
||||
self.next_queue = {}
|
||||
for i = 1, 30 do
|
||||
table.insert(self.next_queue, self:generatePiece())
|
||||
end
|
||||
end
|
||||
|
||||
function AlwaysRandomizer:generatePiece()
|
||||
return "I"
|
||||
return self.piece
|
||||
end
|
||||
|
||||
return AlwaysRandomizer
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local Bag5Randomizer = Randomizer:extend()
|
||||
|
||||
function Bag5Randomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
end
|
||||
|
||||
function Bag5Randomizer:generatePiece()
|
||||
if next(self.bag) == nil then
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
end
|
||||
local x = math.random(table.getn(self.bag))
|
||||
return table.remove(self.bag, x)
|
||||
end
|
||||
|
||||
return Bag5Randomizer
|
||||
@@ -1,24 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local Bag5AltRandomizer = Randomizer:extend()
|
||||
|
||||
function Bag5AltRandomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
self.prev = nil
|
||||
end
|
||||
|
||||
function Bag5AltRandomizer:generatePiece()
|
||||
if next(self.bag) == nil then
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
end
|
||||
local x = math.random(table.getn(self.bag))
|
||||
local temp = table.remove(self.bag, x)
|
||||
if temp == self.prev then
|
||||
local y = math.random(table.getn(self.bag))
|
||||
temp = table.remove(self.bag, y)
|
||||
end
|
||||
self.prev = temp
|
||||
return temp
|
||||
end
|
||||
|
||||
return Bag5AltRandomizer
|
||||
@@ -1,31 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local Bag7NoSZOStartRandomizer = Randomizer:extend()
|
||||
|
||||
function Bag7NoSZOStartRandomizer:shuffleBag()
|
||||
local b = self.bag
|
||||
local ln = #b
|
||||
for i = 1, ln do
|
||||
local j = math.random(i, ln)
|
||||
b[i], b[j] = b[j], b[i]
|
||||
end
|
||||
end
|
||||
|
||||
local function isnotSZO(x) return not(x == "S" or x == "Z" or x == "O") end
|
||||
|
||||
function Bag7NoSZOStartRandomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
repeat
|
||||
self:shuffleBag()
|
||||
until isnotSZO(self.bag[7])
|
||||
end
|
||||
|
||||
function Bag7NoSZOStartRandomizer:generatePiece()
|
||||
if #self.bag == 0 then
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
self:shuffleBag()
|
||||
end
|
||||
return table.remove(self.bag)
|
||||
end
|
||||
|
||||
return Bag7NoSZOStartRandomizer
|
||||
@@ -1,24 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local Bag7Randomizer = Randomizer:extend()
|
||||
|
||||
function Bag7Randomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
|
||||
end
|
||||
|
||||
function Bag7Randomizer:generatePiece()
|
||||
if next(self.extra) == nil then
|
||||
self.extra = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
end
|
||||
if next(self.bag) == nil then
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
table.insert(self.bag, table.remove(self.extra, math.random(table.getn(self.extra))))
|
||||
end
|
||||
local x = math.random(table.getn(self.bag))
|
||||
--print("Bag: "..table.concat(self.bag, ", ").." | Extra: "..table.concat(self.extra, ", "))
|
||||
return table.remove(self.bag, x)
|
||||
end
|
||||
|
||||
return Bag7Randomizer
|
||||
@@ -1,28 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local BagKonoha = Randomizer:extend()
|
||||
|
||||
function BagKonoha:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
self.prev = nil
|
||||
self.allowrepeat = false
|
||||
self.generated = 0
|
||||
end
|
||||
|
||||
function BagKonoha:generatePiece()
|
||||
self.generated = self.generated + 1
|
||||
if #self.bag == 0 then
|
||||
self.bag = {"I", "J", "L", "O", "T"}
|
||||
end
|
||||
local x = math.random(#self.bag)
|
||||
local temp = table.remove(self.bag, x)
|
||||
if temp == self.prev and not self.allowrepeat then
|
||||
local y = math.random(#self.bag)
|
||||
table.insert(self.bag, temp) -- should insert at the end of the bag, bag[y] doesnt change
|
||||
temp = table.remove(self.bag, y)
|
||||
end
|
||||
self.prev = temp
|
||||
return temp
|
||||
end
|
||||
|
||||
return BagKonoha
|
||||
@@ -3,23 +3,17 @@ local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
local History4RollsRandomizer = Randomizer:extend()
|
||||
|
||||
function History4RollsRandomizer:initialize()
|
||||
self.history = {"Z", "Z", "Z", "Z"}
|
||||
self.first = true
|
||||
self.history = {"Z", "S", "Z", "S"}
|
||||
end
|
||||
|
||||
function History4RollsRandomizer:generatePiece()
|
||||
if self.first then
|
||||
self.first = false
|
||||
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||
else
|
||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
for i = 1, 4 do
|
||||
local x = math.random(7)
|
||||
if not inHistory(shapes[x], self.history) or i == 4 then
|
||||
return self:updateHistory(shapes[x])
|
||||
end
|
||||
end
|
||||
end
|
||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
for i = 1, 4 do
|
||||
local x = math.random(7)
|
||||
if not inHistory(shapes[x], self.history) or i == 4 then
|
||||
return self:updateHistory(shapes[x])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function History4RollsRandomizer:updateHistory(shape)
|
||||
|
||||
@@ -4,22 +4,16 @@ local History6RollsRandomizer = Randomizer:extend()
|
||||
|
||||
function History6RollsRandomizer:initialize()
|
||||
self.history = {"Z", "S", "Z", "S"}
|
||||
self.first = true
|
||||
end
|
||||
|
||||
function History6RollsRandomizer:generatePiece()
|
||||
if self.first then
|
||||
self.first = false
|
||||
return self:updateHistory(({"L", "J", "I", "T"})[math.random(4)])
|
||||
else
|
||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
for i = 1, 6 do
|
||||
local x = math.random(7)
|
||||
if not inHistory(shapes[x], self.history) or i == 6 then
|
||||
return self:updateHistory(shapes[x])
|
||||
end
|
||||
end
|
||||
end
|
||||
local shapes = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
for i = 1, 6 do
|
||||
local x = math.random(7)
|
||||
if not inHistory(shapes[x], self.history) or i == 6 then
|
||||
return self:updateHistory(shapes[x])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function History6RollsRandomizer:updateHistory(shape)
|
||||
|
||||
@@ -1,70 +1,38 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local History6Rolls35PoolRandomizer = Randomizer:extend()
|
||||
local History6RollsRandomizer = Randomizer:extend()
|
||||
|
||||
function History6Rolls35PoolRandomizer:initialize()
|
||||
self.first = true
|
||||
function History6RollsRandomizer:initialize()
|
||||
self.history = {"Z", "S", "Z", "S"}
|
||||
self.pool = {
|
||||
"I", "I", "I", "I", "I",
|
||||
"T", "T", "T", "T", "T",
|
||||
"L", "L", "L", "L", "L",
|
||||
"J", "J", "J", "J", "J",
|
||||
"S", "S", "S", "S", "S",
|
||||
"Z", "Z", "Z", "Z", "Z",
|
||||
"O", "O", "O", "O", "O",
|
||||
self.bag_counts = {
|
||||
I = 5, J = 5, L = 5, O = 5, S = 3, T = 5, Z = 3
|
||||
}
|
||||
self.droughts = {
|
||||
I = 0,
|
||||
T = 0,
|
||||
L = 0,
|
||||
J = 0,
|
||||
S = 0,
|
||||
Z = 0,
|
||||
O = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function History6Rolls35PoolRandomizer:generatePiece()
|
||||
local index, x
|
||||
if self.first then
|
||||
local prevent = {"S", "Z", "O"}
|
||||
repeat
|
||||
index = math.random(#self.pool)
|
||||
x = self.pool[index]
|
||||
until not inHistory(x, prevent)
|
||||
self.first = false
|
||||
else
|
||||
for i = 1, 6 do
|
||||
index = math.random(#self.pool)
|
||||
x = self.pool[index]
|
||||
if not inHistory(x, self.history) or i == 6 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
self.pool[index] = self:updateHistory(x)
|
||||
return x
|
||||
function History6RollsRandomizer:getBagPiece(n)
|
||||
for shape, count in pairs(self.bag_counts) do
|
||||
n = n - count
|
||||
if n <= 0 then
|
||||
return shape
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function History6Rolls35PoolRandomizer:updateHistory(shape)
|
||||
table.remove(self.history, 1)
|
||||
function History6RollsRandomizer:generatePiece()
|
||||
for i = 1, 6 do
|
||||
local x = self:getBagPiece(math.random(31))
|
||||
if not inHistory(x, self.history) or i == 6 then
|
||||
return self:updateHistory(x)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function History6RollsRandomizer:updateHistory(shape)
|
||||
self.bag_counts[shape] = self.bag_counts[shape] - 1
|
||||
local replaced_piece = table.remove(self.history, 1)
|
||||
table.insert(self.history, shape)
|
||||
|
||||
local highdrought
|
||||
local highdroughtcount = 0
|
||||
for k, v in pairs(self.droughts) do
|
||||
if k == shape then
|
||||
self.droughts[k] = 0
|
||||
else
|
||||
self.droughts[k] = v + 1
|
||||
if v >= highdroughtcount then
|
||||
highdrought = k
|
||||
highdroughtcount = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return highdrought
|
||||
self.bag_counts[replaced_piece] = self.bag_counts[replaced_piece] + 1
|
||||
return shape
|
||||
end
|
||||
|
||||
function inHistory(piece, history)
|
||||
@@ -76,4 +44,4 @@ function inHistory(piece, history)
|
||||
return false
|
||||
end
|
||||
|
||||
return History6Rolls35PoolRandomizer
|
||||
return History6RollsRandomizer
|
||||
|
||||
@@ -4,10 +4,15 @@ local Randomizer = Object:extend()
|
||||
|
||||
function Randomizer:new()
|
||||
self:initialize()
|
||||
self.next_queue = {}
|
||||
for i = 1, 30 do
|
||||
table.insert(self.next_queue, self:generatePiece())
|
||||
end
|
||||
end
|
||||
|
||||
function Randomizer:nextPiece()
|
||||
return self:generatePiece()
|
||||
table.insert(self.next_queue, self:generatePiece())
|
||||
return table.remove(self.next_queue, 1)
|
||||
end
|
||||
|
||||
function Randomizer:initialize()
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local RecursiveRandomizer = Randomizer:extend()
|
||||
|
||||
function RecursiveRandomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
end
|
||||
|
||||
function RecursiveRandomizer:generatePiece()
|
||||
--if next(self.bag) == nil then
|
||||
-- self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
--end
|
||||
local x = math.random(table.getn(self.bag) + 1)
|
||||
while x == table.getn(self.bag) + 1 do
|
||||
--print("Refill piece pulled")
|
||||
table.insert(self.bag, "I")
|
||||
table.insert(self.bag, "J")
|
||||
table.insert(self.bag, "L")
|
||||
table.insert(self.bag, "O")
|
||||
table.insert(self.bag, "S")
|
||||
table.insert(self.bag, "T")
|
||||
table.insert(self.bag, "Z")
|
||||
x = math.random(table.getn(self.bag) + 1)
|
||||
end
|
||||
--print("Number of pieces in bag: "..table.getn(self.bag))
|
||||
--print("Bag: "..table.concat(self.bag, ", "))
|
||||
return table.remove(self.bag, x)
|
||||
end
|
||||
|
||||
return RecursiveRandomizer
|
||||
@@ -1,19 +0,0 @@
|
||||
local Randomizer = require 'tetris.randomizers.randomizer'
|
||||
|
||||
local SegaRandomizer = Randomizer:extend()
|
||||
|
||||
function SegaRandomizer:initialize()
|
||||
self.bag = {"I", "J", "L", "O", "S", "T", "Z"}
|
||||
self.sequence = {}
|
||||
for i = 1, 1000 do
|
||||
self.sequence[i] = self.bag[math.random(table.getn(self.bag))]
|
||||
end
|
||||
self.counter = 0
|
||||
end
|
||||
|
||||
function SegaRandomizer:generatePiece()
|
||||
self.counter = self.counter + 1
|
||||
return self.sequence[self.counter % 1000 + 1]
|
||||
end
|
||||
|
||||
return SegaRandomizer
|
||||
@@ -17,10 +17,10 @@ ARS.spawn_positions = {
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
I = { x=2, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
@@ -81,19 +81,11 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||
) and (
|
||||
piece.rotation == 0 or piece.rotation == 2
|
||||
) then
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
if offset.x == 0 then
|
||||
return
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
) and (
|
||||
grid:isOccupied(piece.position.x, piece.position.y) or
|
||||
grid:isOccupied(piece.position.x, piece.position.y - 1) or
|
||||
grid:isOccupied(piece.position.x, piece.position.y - 2)
|
||||
) then return end
|
||||
|
||||
-- kick right, kick left
|
||||
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
|
||||
@@ -110,7 +102,7 @@ function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
179
tetris/rulesets/arika_ace.lua
Executable file → Normal file
179
tetris/rulesets/arika_ace.lua
Executable file → Normal file
@@ -1,164 +1,14 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
local ArikaTI = require 'tetris.rulesets.arika_ti'
|
||||
|
||||
local ARS = Ruleset:extend()
|
||||
local ARS = ArikaTI:extend()
|
||||
|
||||
ARS.name = "ACE-ARS"
|
||||
ARS.hash = "ArikaACE"
|
||||
|
||||
ARS.colourscheme = {
|
||||
I = "C",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "G",
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
}
|
||||
|
||||
ARS.softdrop_lock = false
|
||||
ARS.harddrop_lock = true
|
||||
|
||||
ARS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
ARS.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=-1}, {x=1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=-1, y=0} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=-2}, {x=0, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-1}, {x=0, y=-2} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=-1, y=-1}, {x=0, y=-2} },
|
||||
},
|
||||
Z={
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
-- O doesn't kick
|
||||
if (piece.shape == "O") then return end
|
||||
|
||||
-- center column rule
|
||||
if (
|
||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||
) and (
|
||||
piece.rotation == 0 or piece.rotation == 2
|
||||
) then
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
if offset.x == 0 then
|
||||
return
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if piece.shape == "I" then
|
||||
-- special kick rules for I
|
||||
if new_piece.rotation == 0 or new_piece.rotation == 2 then
|
||||
-- kick right, right2, left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then
|
||||
-- kick up, up2
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece.floorkick = 1
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece.floorkick = 1
|
||||
end
|
||||
end
|
||||
else
|
||||
-- kick right, kick left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
elseif piece.shape == "T"
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
-- T floorkick
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
ARS.name = "Ace-ARS"
|
||||
ARS.hash = "ArikaAce"
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
piece.rotate_counter = 0
|
||||
piece.move_counter = 0
|
||||
end
|
||||
|
||||
function ARS:onPieceDrop(piece, grid)
|
||||
@@ -168,24 +18,15 @@ 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 >= 127 then
|
||||
piece.move_counter = piece.move_counter + 1
|
||||
if piece.move_counter >= 128 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 127 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
self:onPieceMove(piece, grid)
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
return ARS
|
||||
@@ -1,178 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local ARS = Ruleset:extend()
|
||||
|
||||
ARS.name = "ACE-ARS2"
|
||||
ARS.hash = "ArikaACE2"
|
||||
|
||||
ARS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
ARS.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=-1}, {x=1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=-1, y=0} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=-2}, {x=0, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-1}, {x=0, y=-2} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=-1, y=-1}, {x=0, y=-2} },
|
||||
},
|
||||
Z={
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-2}, {x=1, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
-- O doesn't kick
|
||||
if (piece.shape == "O") then return end
|
||||
|
||||
-- center column rule
|
||||
if (
|
||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||
) and (
|
||||
piece.rotation == 0 or piece.rotation == 2
|
||||
) then
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
if offset.x == 0 then
|
||||
return
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if piece.shape == "I" then
|
||||
-- special kick rules for I
|
||||
if new_piece.rotation == 0 or new_piece.rotation == 2 then
|
||||
-- kick right, right2, left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=2, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=2, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
elseif piece:isDropBlocked(grid) and (new_piece.rotation == 1 or new_piece.rotation == 3) and piece.floorkick == 0 then
|
||||
-- kick up, up2
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0, y=-1})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece.floorkick = 1
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=0, y=-2})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-2})
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece.floorkick = 1
|
||||
end
|
||||
end
|
||||
else
|
||||
-- kick right, kick left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
elseif piece.shape == "T"
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
-- T floorkick
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
end
|
||||
|
||||
function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
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 >= 127 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 127 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
@@ -1,188 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
SRS.name = "ACE-SRS"
|
||||
SRS.hash = "ACE Standard"
|
||||
SRS.world = true
|
||||
SRS.colourscheme = {
|
||||
I = "C",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "G",
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
}
|
||||
SRS.softdrop_lock = false
|
||||
SRS.harddrop_lock = true
|
||||
|
||||
SRS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
SRS.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
|
||||
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
SRS.wallkicks_3x3 = {
|
||||
[0]={
|
||||
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||
[2]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||
[3]={{x=0, y=1}, {x=0, y=-1}},
|
||||
},
|
||||
[2]={
|
||||
[0]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||
[1]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||
},
|
||||
};
|
||||
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
[2]={},
|
||||
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 2}},
|
||||
[2]={{x=-1, y= 0}, {x= 2, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
|
||||
[3]={},
|
||||
},
|
||||
[2]={
|
||||
[0]={},
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 1}},
|
||||
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}},
|
||||
[1]={},
|
||||
[2]={{x= 1, y= 0}, {x=-2, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
},
|
||||
};
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
local kicks
|
||||
if piece.shape == "O" then
|
||||
return
|
||||
elseif piece.shape == "I" then
|
||||
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||
else
|
||||
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||
end
|
||||
|
||||
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
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.manipulations = 0
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 127 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 127 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 3 end
|
||||
return SRS
|
||||
@@ -17,10 +17,10 @@ ARS.spawn_positions = {
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
I = { x=2, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
@@ -84,19 +84,11 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
piece.shape == "J" or piece.shape == "T" or piece.shape == "L"
|
||||
) and (
|
||||
piece.rotation == 0 or piece.rotation == 2
|
||||
) then
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
if offset.x == 0 then
|
||||
return
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
) and (
|
||||
grid:isOccupied(piece.position.x, piece.position.y) or
|
||||
grid:isOccupied(piece.position.x, piece.position.y - 1) or
|
||||
grid:isOccupied(piece.position.x, piece.position.y - 2)
|
||||
) then return end
|
||||
|
||||
if piece.shape == "I" then
|
||||
-- special kick rules for I
|
||||
@@ -124,21 +116,13 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
piece.floorkick = 1
|
||||
end
|
||||
end
|
||||
else
|
||||
-- kick right, kick left
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=-1, y=0})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
elseif piece.shape == "T"
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
-- T floorkick
|
||||
piece.floorkick = piece.floorkick + 1
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0, y=-1})
|
||||
end
|
||||
elseif piece.shape ~= "I" then
|
||||
-- kick right, kick left
|
||||
if (grid:canPlacePiece(new_piece:withOffset({x=1, y=0}))) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=1, y=0})
|
||||
elseif (grid:canPlacePiece(new_piece:withOffset({x=-1, y=0}))) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=-1, y=0})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -151,7 +135,7 @@ function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -5,26 +5,25 @@ local CRS = Ruleset:extend()
|
||||
|
||||
CRS.name = "Cambridge"
|
||||
CRS.hash = "Cambridge"
|
||||
CRS.world = true
|
||||
|
||||
CRS.spawn_positions = {
|
||||
I = { x=5, y=4 },
|
||||
J = { x=4, y=5 },
|
||||
L = { x=4, y=5 },
|
||||
O = { x=5, y=5 },
|
||||
S = { x=4, y=5 },
|
||||
S = { x=4, y=4 },
|
||||
T = { x=4, y=5 },
|
||||
Z = { x=4, y=5 },
|
||||
Z = { x=4, y=4 },
|
||||
}
|
||||
|
||||
CRS.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
I = { x=2, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
S = { x=2, y=2 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
Z = { x=2, y=2 },
|
||||
}
|
||||
|
||||
CRS.block_offsets = {
|
||||
@@ -53,10 +52,10 @@ CRS.block_offsets = {
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1}, {x=-1, y=-2} },
|
||||
{ {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1}, {x=-1, y=-2} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=1}, {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=1}, {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
@@ -65,10 +64,10 @@ CRS.block_offsets = {
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
{ {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
{ {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,10 +362,6 @@ function CRS:attemptRotate(new_inputs, piece, grid, initial)
|
||||
end
|
||||
|
||||
if rot_dir == 0 then return end
|
||||
|
||||
if self.world and config.gamesettings.world_reverse == 2 then
|
||||
rot_dir = 4 - rot_dir
|
||||
end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
self:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
@@ -403,6 +398,7 @@ function CRS:onPieceDrop(piece, grid)
|
||||
end
|
||||
|
||||
function CRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.move_counter = piece.move_counter + 1
|
||||
if piece.move_counter >= 24 then
|
||||
@@ -412,6 +408,7 @@ function CRS:onPieceMove(piece, grid)
|
||||
end
|
||||
|
||||
function CRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.rotate_counter = piece.rotate_counter + 1
|
||||
if piece.rotate_counter >= 12 then
|
||||
@@ -420,6 +417,4 @@ function CRS:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function CRS:getDefaultOrientation() return 1 end -- downward facing pieces by default
|
||||
|
||||
return CRS
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local CRAP = Ruleset:extend()
|
||||
|
||||
CRAP.name = "C.R.A.P."
|
||||
CRAP.hash = "Completely Random Auto-Positioner"
|
||||
CRAP.world = true
|
||||
CRAP.colors={"C","O","M","R","G","Y","B"}
|
||||
CRAP.colourscheme = {
|
||||
I = CRAP.colors[math.ceil(math.random(7))],
|
||||
L = CRAP.colors[math.ceil(math.random(7))],
|
||||
J = CRAP.colors[math.ceil(math.random(7))],
|
||||
S = CRAP.colors[math.ceil(math.random(7))],
|
||||
Z = CRAP.colors[math.ceil(math.random(7))],
|
||||
O = CRAP.colors[math.ceil(math.random(7))],
|
||||
T = CRAP.colors[math.ceil(math.random(7))],
|
||||
}
|
||||
CRAP.softdrop_lock = true
|
||||
CRAP.harddrop_lock = false
|
||||
|
||||
CRAP.enable_IRS_wallkicks = true
|
||||
|
||||
CRAP.spawn_positions = {
|
||||
I = { x=5, y=4 },
|
||||
J = { x=4, y=5 },
|
||||
L = { x=4, y=5 },
|
||||
O = { x=5, y=5 },
|
||||
S = { x=4, y=5 },
|
||||
T = { x=4, y=5 },
|
||||
Z = { x=4, y=5 },
|
||||
}
|
||||
|
||||
CRAP.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
}
|
||||
|
||||
CRAP.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
|
||||
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function CRAP:attemptRotate(new_inputs, piece, grid, initial)
|
||||
local rot_dir = 0
|
||||
|
||||
if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
|
||||
rot_dir = 3
|
||||
elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
|
||||
rot_dir = 1
|
||||
elseif (new_inputs["rotate_180"]) then
|
||||
rot_dir = self:get180RotationValue()
|
||||
end
|
||||
|
||||
if rot_dir == 0 then return end
|
||||
if self.world and config.gamesettings.world_reverse == 2 then
|
||||
rot_dir = 4 - rot_dir
|
||||
end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
|
||||
self:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
end
|
||||
|
||||
function CRAP:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
for i=1,20 do
|
||||
dx=math.floor(math.random(11))-5
|
||||
dy=math.floor(math.random(11))-5
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=dx, y=dy})) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=dy})
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function CRAP:onPieceCreate(piece, grid)
|
||||
CRAP:randomizeColours()
|
||||
piece.manipulations = 0
|
||||
piece.rotations = 0
|
||||
end
|
||||
|
||||
function CRAP:onPieceDrop(piece, grid)
|
||||
CRAP:randomizeColours()
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function CRAP:onPieceMove(piece, grid)
|
||||
CRAP:randomizeColours()
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 10 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CRAP:onPieceRotate(piece, grid)
|
||||
CRAP:randomizeColours()
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.rotations = piece.rotations + 1
|
||||
if piece.rotations >= 8 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CRAP:get180RotationValue() return 2 end
|
||||
|
||||
function CRAP:randomizeColours()
|
||||
CRAP.colourscheme = {
|
||||
I = CRAP.colors[math.ceil(math.random(7))],
|
||||
L = CRAP.colors[math.ceil(math.random(7))],
|
||||
J = CRAP.colors[math.ceil(math.random(7))],
|
||||
S = CRAP.colors[math.ceil(math.random(7))],
|
||||
Z = CRAP.colors[math.ceil(math.random(7))],
|
||||
O = CRAP.colors[math.ceil(math.random(7))],
|
||||
T = CRAP.colors[math.ceil(math.random(7))],
|
||||
}
|
||||
end
|
||||
|
||||
return CRAP
|
||||
@@ -1,153 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local DTET = Ruleset:extend()
|
||||
|
||||
DTET.name = "D.R.S."
|
||||
DTET.hash = "DTET"
|
||||
|
||||
DTET.spawn_positions = {
|
||||
I = { x=5, y=4 },
|
||||
J = { x=4, y=5 },
|
||||
L = { x=4, y=5 },
|
||||
O = { x=5, y=5 },
|
||||
S = { x=4, y=5 },
|
||||
T = { x=4, y=5 },
|
||||
Z = { x=4, y=5 },
|
||||
}
|
||||
|
||||
DTET.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
}
|
||||
|
||||
DTET.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=-1, y=-1}, {x=-1, y=-2}, {x=-1, y=0}, {x=-1, y=1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=0, y=1} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=1, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=-2}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=-1}, {x=1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=-1, y=0} },
|
||||
{ {x=0, y=-1}, {x=-1, y=-2}, {x=0, y=-2}, {x=0, y=0} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=-2}, {x=0, y=-1}, {x=1, y=0}, {x=0, y=0} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=-2}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
{ {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} },
|
||||
{ {x=1, y=0}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=-2} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=-1}, {x=-1, y=-1}, {x=1, y=-1}, {x=0, y=0} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=-1, y=-1}, {x=0, y=-2} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=-1}, {x=0, y=0}, {x=1, y=-1}, {x=0, y=-2} },
|
||||
},
|
||||
Z={
|
||||
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
{ {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2} },
|
||||
{ {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
{ {x=1, y=-2}, {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0} },
|
||||
}
|
||||
}
|
||||
|
||||
-- clockwise kicks: {{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
-- counterclockwise kicks: {{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
|
||||
DTET.wallkicks_3x3 = {
|
||||
[0]={
|
||||
[1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[2]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
[2]={
|
||||
[0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[3]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
};
|
||||
|
||||
DTET.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[2]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
[2]={
|
||||
[0]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[1]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[3]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=1, y=1}, {x=-1, y=1}},
|
||||
[1]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
[2]={{x=-1, y=0}, {x=1, y=0}, {x=0, y=1}, {x=-1, y=1}, {x=1, y=1}},
|
||||
},
|
||||
};
|
||||
|
||||
function DTET:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
local kicks
|
||||
if piece.shape == "O" then
|
||||
return
|
||||
elseif piece.shape == "I" then
|
||||
kicks = DTET.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||
else
|
||||
kicks = DTET.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||
end
|
||||
|
||||
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
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DTET:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function DTET:getDefaultOrientation() return 1 end
|
||||
|
||||
return DTET
|
||||
@@ -1,45 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local ARS = require 'tetris.rulesets.arika'
|
||||
|
||||
local EHeart = ARS:extend()
|
||||
|
||||
EHeart.name = "E-Heart ARS"
|
||||
EHeart.hash = "EHeartARS"
|
||||
|
||||
function EHeart:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
-- I and O don't kick
|
||||
if (piece.shape == "I" or piece.shape == "O") then return end
|
||||
|
||||
-- center column rule (kicks)
|
||||
local offsets = new_piece:getBlockOffsets()
|
||||
table.sort(offsets, function(A, B) return A.y < B.y or A.y == B.y and A.x < B.y end)
|
||||
for index, offset in pairs(offsets) do
|
||||
if grid:isOccupied(piece.position.x + offset.x, piece.position.y + offset.y) then
|
||||
-- individual checks for all 9 cells, in the given order
|
||||
if offset.y < 0 then
|
||||
if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid)
|
||||
elseif offset.x == 0 then return
|
||||
elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end
|
||||
elseif offset.y == 0 then
|
||||
if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid)
|
||||
elseif offset.x == 0 then return
|
||||
elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end
|
||||
elseif offset.y > 0 then
|
||||
if offset.x < 0 then self:lateralKick(1, piece, new_piece, rot_dir, grid)
|
||||
elseif offset.x == 0 then return
|
||||
elseif offset.x > 0 then self:lateralKick(-1, piece, new_piece, rot_dir, grid) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function EHeart:lateralKick(dx, piece, new_piece, rot_dir, grid)
|
||||
if (grid:canPlacePiece(new_piece:withOffset({x=dx, y=0}))) then
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=dx, y=0})
|
||||
self:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
return EHeart
|
||||
@@ -1,102 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local SRS = require 'tetris.rulesets.standard_exp'
|
||||
|
||||
local PPTPRS = SRS:extend()
|
||||
|
||||
PPTPRS.name = "PPTPRS"
|
||||
PPTPRS.hash = "Puyo Tetris Pentos"
|
||||
|
||||
PPTPRS.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0}, {x=-3, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2}, {x=0, y=-2} },
|
||||
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1}, {x=2, y=1} },
|
||||
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2}, {x=-1, y=3} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1}, {x=0, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1}, {x=-1, y=0} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1}, {x=1, y=0} },
|
||||
},
|
||||
L={
|
||||
{ {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=1}, {x=1, y=1}, {x=-1, y=-1} },
|
||||
{ {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=-1}, {x=-1, y=-1}, {x=1, y=1} },
|
||||
},
|
||||
O={
|
||||
{ {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=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=-2, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=-2} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=0} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0} },
|
||||
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=0, y=-2} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0}, {x=2, y=0} },
|
||||
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1}, {x=0, y=2} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1}, {x=0, y=-2} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0}, {x=2, y=0} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0}, {x=-2, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0}, {x=1, y=1} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1}, {x=-1, y=1} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1}, {x=1, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
PPTPRS.wallkicks_O = {
|
||||
[0]={
|
||||
[1]={{x=0, y=1}},
|
||||
[2]={{x=0, y=1}},
|
||||
[3]={{x=-1, y=0}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=0, y=-1}},
|
||||
[2]={{x=-1, y=0}},
|
||||
[3]={{x=-1, y=0}},
|
||||
},
|
||||
[2]={
|
||||
[0]={{x=0, y=-1}},
|
||||
[1]={{x=1, y=0}},
|
||||
[3]={{x=0, y=-1}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=1, y=0}},
|
||||
[1]={{x=1, y=0}},
|
||||
[2]={{x=0, y=1}},
|
||||
},
|
||||
}
|
||||
|
||||
function PPTPRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
local kicks
|
||||
|
||||
if piece.shape == "O" then
|
||||
kicks = PPTPRS.wallkicks_O[piece.rotation][new_piece.rotation]
|
||||
elseif piece.shape == "I" then
|
||||
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||
else
|
||||
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||
end
|
||||
|
||||
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
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return PPTPRS
|
||||
@@ -6,47 +6,10 @@ local Ruleset = Object:extend()
|
||||
Ruleset.name = ""
|
||||
Ruleset.hash = ""
|
||||
|
||||
-- Arika-type ruleset defaults
|
||||
Ruleset.world = false
|
||||
Ruleset.colourscheme = {
|
||||
I = "R",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "M",
|
||||
Z = "G",
|
||||
O = "Y",
|
||||
T = "C",
|
||||
}
|
||||
Ruleset.softdrop_lock = true
|
||||
Ruleset.harddrop_lock = false
|
||||
|
||||
Ruleset.enable_IRS_wallkicks = false
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function Ruleset:new()
|
||||
blocks["bone"] = (not self.world) and
|
||||
{
|
||||
R = love.graphics.newImage("res/img/bone.png"),
|
||||
O = love.graphics.newImage("res/img/bone.png"),
|
||||
Y = love.graphics.newImage("res/img/bone.png"),
|
||||
G = love.graphics.newImage("res/img/bone.png"),
|
||||
C = love.graphics.newImage("res/img/bone.png"),
|
||||
B = love.graphics.newImage("res/img/bone.png"),
|
||||
M = love.graphics.newImage("res/img/bone.png"),
|
||||
X = love.graphics.newImage("res/img/bone.png"),
|
||||
} or {
|
||||
R = love.graphics.newImage("res/img/bonew.png"),
|
||||
O = love.graphics.newImage("res/img/bonew.png"),
|
||||
Y = love.graphics.newImage("res/img/bonew.png"),
|
||||
G = love.graphics.newImage("res/img/bonew.png"),
|
||||
C = love.graphics.newImage("res/img/bonew.png"),
|
||||
B = love.graphics.newImage("res/img/bonew.png"),
|
||||
M = love.graphics.newImage("res/img/bonew.png"),
|
||||
X = love.graphics.newImage("res/img/bonew.png"),
|
||||
}
|
||||
end
|
||||
|
||||
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
local new_inputs = {}
|
||||
|
||||
@@ -76,9 +39,6 @@ function Ruleset:attemptRotate(new_inputs, piece, grid, initial)
|
||||
end
|
||||
|
||||
if rot_dir == 0 then return end
|
||||
if self.world and config.gamesettings.world_reverse == 2 then
|
||||
rot_dir = 4 - rot_dir
|
||||
end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
|
||||
@@ -96,16 +56,16 @@ function Ruleset:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
-- do nothing in default ruleset
|
||||
end
|
||||
|
||||
function Ruleset:movePiece(piece, grid, move)
|
||||
function Ruleset:movePiece(piece, grid, move, instant)
|
||||
local x = piece.position.x
|
||||
if move == "left" then
|
||||
piece:moveInGrid({x=-1, y=0}, 1, grid)
|
||||
elseif move == "speedleft" then
|
||||
piece:moveInGrid({x=-1, y=0}, 10, grid)
|
||||
piece:moveInGrid({x=-1, y=0}, 1, grid, false)
|
||||
elseif move == "right" then
|
||||
piece:moveInGrid({x=1, y=0}, 1, grid)
|
||||
piece:moveInGrid({x=1, y=0}, 1, grid, false)
|
||||
elseif move == "speedleft" then
|
||||
piece:moveInGrid({x=-1, y=0}, 10, grid, instant)
|
||||
elseif move == "speedright" then
|
||||
piece:moveInGrid({x=1, y=0}, 10, grid)
|
||||
piece:moveInGrid({x=1, y=0}, 10, grid, instant)
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
@@ -157,12 +117,10 @@ function Ruleset:initializePiece(
|
||||
else
|
||||
spawn_positions = self.spawn_positions
|
||||
end
|
||||
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
}, self.block_offsets, 0, 0, data.skin, big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
@@ -180,7 +138,7 @@ function Ruleset:processPiece(
|
||||
hard_drop_enabled, additive_gravity
|
||||
)
|
||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false)
|
||||
self:movePiece(piece, grid, move)
|
||||
self:movePiece(piece, grid, move, gravity >= 20)
|
||||
self:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity
|
||||
|
||||
@@ -3,20 +3,10 @@ local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
SRS.name = "Ti-World"
|
||||
SRS.hash = "Bad I-kicks"
|
||||
SRS.world = true
|
||||
SRS.colourscheme = {
|
||||
I = "C",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "G",
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
}
|
||||
SRS.softdrop_lock = false
|
||||
SRS.harddrop_lock = true
|
||||
SRS.name = "SRS"
|
||||
SRS.hash = "Standard"
|
||||
|
||||
SRS.enable_IRS_wallkicks = true
|
||||
|
||||
SRS.spawn_positions = {
|
||||
I = { x=5, y=4 },
|
||||
@@ -29,10 +19,10 @@ SRS.spawn_positions = {
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=3, y=2 },
|
||||
I = { x=2, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
@@ -108,24 +98,24 @@ SRS.wallkicks_3x3 = {
|
||||
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
[2]={},
|
||||
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
|
||||
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 2}},
|
||||
[2]={{x=-1, y= 0}, {x= 2, y= 0}, {x=-1, y=-2}, {x= 2, y= 1}},
|
||||
[3]={},
|
||||
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||
},
|
||||
[2]={
|
||||
[0]={},
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 1}},
|
||||
[3]={{x= 2, y= 0}, {x=-1, y= 0}, {x= 2, y=-1}, {x=-1, y= 1}},
|
||||
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=-2, y= 0}, {x= 1, y= 0}, {x=-2, y=-1}, {x= 1, y= 2}},
|
||||
[1]={},
|
||||
[2]={{x= 1, y= 0}, {x=-2, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -157,8 +147,8 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.manipulations = 0
|
||||
piece.rotations = 0
|
||||
piece.rotate_counter = 0
|
||||
piece.move_counter = 0
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
@@ -168,8 +158,8 @@ end
|
||||
function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 10 then
|
||||
piece.move_counter = piece.move_counter + 1
|
||||
if piece.move_counter >= 24 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
@@ -178,12 +168,11 @@ end
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.rotations = piece.rotations + 1
|
||||
if piece.rotations >= 8 then
|
||||
piece.rotate_counter = piece.rotate_counter + 1
|
||||
if piece.rotate_counter >= 12 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 3 end
|
||||
return SRS
|
||||
@@ -1,206 +0,0 @@
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Ruleset = require 'tetris.rulesets.ruleset'
|
||||
|
||||
local SRS = Ruleset:extend()
|
||||
|
||||
SRS.name = "Guideline SRS"
|
||||
SRS.hash = "Standard"
|
||||
SRS.world = true
|
||||
SRS.colourscheme = {
|
||||
I = "C",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "G",
|
||||
Z = "R",
|
||||
O = "Y",
|
||||
T = "M",
|
||||
}
|
||||
SRS.softdrop_lock = false
|
||||
SRS.harddrop_lock = true
|
||||
|
||||
SRS.enable_IRS_wallkicks = true
|
||||
|
||||
SRS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
SRS.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-2, y=0}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=0, y=2} },
|
||||
{ {x=0, y=1}, {x=-1, y=1}, {x=-2, y=1}, {x=1, y=1} },
|
||||
{ {x=-1, y=0}, {x=-1, y=-1}, {x=-1, y=1}, {x=-1, y=2} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=-1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1} , {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=1} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=1, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=-1, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=-1} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=-1, y=-1}, {x=0, y=-1} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=1, y=1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
{ {x=-1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=-1, y=-1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=-1, y=-1}, {x=0, y=-1}, {x=0, y=0}, {x=1, y=0} },
|
||||
{ {x=1, y=-1}, {x=1, y=0}, {x=0, y=0}, {x=0, y=1} },
|
||||
{ {x=1, y=1}, {x=0, y=1}, {x=0, y=0}, {x=-1, y=0} },
|
||||
{ {x=-1, y=1}, {x=-1, y=0}, {x=0, y=0}, {x=0, y=-1} },
|
||||
}
|
||||
}
|
||||
|
||||
SRS.wallkicks_3x3 = {
|
||||
[0]={
|
||||
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||
[2]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||
[2]={{x=1, y=0}, {x=1, y=1}, {x=0, y=-2}, {x=1, y=-2}},
|
||||
[3]={{x=0, y=1}, {x=0, y=-1}},
|
||||
},
|
||||
[2]={
|
||||
[0]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[1]={{x=-1, y=0}, {x=-1, y=-1}, {x=0, y=2}, {x=-1, y=2}},
|
||||
[3]={{x=1, y=0}, {x=1, y=-1}, {x=0, y=2}, {x=1, y=2}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||
[1]={{x=0, y=1}, {x=0, y=-1}},
|
||||
[2]={{x=-1, y=0}, {x=-1, y=1}, {x=0, y=-2}, {x=-1, y=-2}},
|
||||
},
|
||||
};
|
||||
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
[2]={},
|
||||
[3]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||
},
|
||||
[1]={
|
||||
[0]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||
[2]={{x=-1, y=0}, {x=2, y=0}, {x=-1, y=-2}, {x=2, y=1}},
|
||||
[3]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||
},
|
||||
[2]={
|
||||
[0]={},
|
||||
[1]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||
[3]={{x=2, y=0}, {x=-1, y=0}, {x=2, y=-1}, {x=-1, y=2}},
|
||||
},
|
||||
[3]={
|
||||
[0]={{x=1, y=0}, {x=-2, y=0}, {x=1, y=2}, {x=-2, y=-1}},
|
||||
[1]={{x=0, y=1}, {x=0, y=-1}, {x=0, y=2}, {x=0, y=-2}},
|
||||
[2]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
},
|
||||
};
|
||||
|
||||
function SRS:check_new_low(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.lowest_y = y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
|
||||
local kicks
|
||||
if piece.shape == "O" then
|
||||
return
|
||||
elseif piece.shape == "I" then
|
||||
kicks = SRS.wallkicks_line[piece.rotation][new_piece.rotation]
|
||||
else
|
||||
kicks = SRS.wallkicks_3x3[piece.rotation][new_piece.rotation]
|
||||
end
|
||||
|
||||
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
|
||||
piece:setRelativeRotation(rot_dir)
|
||||
piece:setOffset(offset)
|
||||
self:onPieceRotate(piece, grid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.manipulations = 0
|
||||
piece.lowest_y = -math.huge
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
self:check_new_low(piece)
|
||||
if piece.manipulations >= 15 and piece:isDropBlocked(grid) then
|
||||
piece.locked = true
|
||||
else
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 15 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
self:check_new_low(piece)
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece:isDropBlocked(grid) then
|
||||
if piece.manipulations >= 15 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return SRS
|
||||
30
tetris/rulesets/standard_ti.lua
Normal file
30
tetris/rulesets/standard_ti.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
local Standard = require 'tetris.rulesets.standard'
|
||||
|
||||
local SRS = Standard:extend()
|
||||
|
||||
SRS.name = "Ti-SRS"
|
||||
SRS.hash = "StandardTI"
|
||||
|
||||
function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.move_counter = piece.move_counter + 1
|
||||
if piece.move_counter >= 10 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.rotate_counter = piece.rotate_counter + 1
|
||||
if piece.rotate_counter >= 8 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
|
||||
return SRS
|
||||
Reference in New Issue
Block a user