mirror of
https://github.com/SashLilac/cambridge.git
synced 2025-05-13 20:21:25 -05:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
062ab2005e | ||
|
|
468025fc80 | ||
|
|
c8544975d6 | ||
|
|
6776229bfb | ||
|
|
84b4dc5073 | ||
|
|
35dafb6615 | ||
|
|
3641d85fcb | ||
|
|
9b89c4d1de | ||
|
|
2dba120919 | ||
|
|
9224f271b1 | ||
|
|
febb5d546c | ||
|
|
c6482c423e | ||
|
|
6beb313c6b | ||
|
|
eb70f55b6e | ||
|
|
0badcde9ad | ||
|
|
6f39b591d3 | ||
|
|
129237f0b0 | ||
|
|
741c246244 | ||
|
|
b5937af8b2 | ||
|
|
33b8533d8e | ||
|
|
69959ff687 | ||
|
|
f91cd99dfd | ||
|
|
be59727ca5 | ||
|
|
cca295066c | ||
|
|
f2862b4d93 | ||
|
|
2aafd30253 | ||
|
|
b27ba335ba | ||
|
|
33244736b8 | ||
|
|
285108ca08 | ||
|
|
4b1fed727c | ||
|
|
9fca272e8d | ||
|
|
5a21c8244b | ||
|
|
4923b2e2d4 | ||
|
|
8810f24e7a | ||
|
|
57a9f6ef55 | ||
|
|
342036bc28 | ||
|
|
78dcfe43c4 | ||
|
|
cdf6b5cf33 | ||
|
|
e6a60b0021 | ||
|
|
5f29c987f2 | ||
|
|
608d75b1ac | ||
|
|
1427c0d19e | ||
|
|
e221a91d73 | ||
|
|
bdcd25b82c | ||
|
|
a5158e0994 | ||
|
|
d946b17e13 | ||
|
|
69a5c0a21a | ||
|
|
b6423c3335 | ||
|
|
5b960d7291 | ||
|
|
54f4b0b890 | ||
|
|
8c62f321a0 | ||
|
|
fdffd2cd9a | ||
|
|
8ddf468121 | ||
|
|
8e77407ff2 | ||
|
|
92c852d178 | ||
|
|
f658ed63f2 | ||
|
|
c2d1c1183c | ||
|
|
36c568feaf | ||
|
|
bf30fcefbd | ||
|
|
d9f5bd16d7 | ||
|
|
d978ff8d87 | ||
|
|
b47d0f36b9 | ||
|
|
abc210c69c | ||
|
|
436e4ac861 | ||
|
|
a48d7c67b5 | ||
|
|
f6ca79ff91 | ||
|
|
4eb3901610 | ||
|
|
3f7fc4b622 | ||
|
|
ac7ae91c39 | ||
|
|
0c8e910245 | ||
|
|
6233ffb12d | ||
|
|
1f78bb9e99 | ||
|
|
a125c09106 | ||
|
|
090ffa5126 | ||
|
|
12a6f42198 | ||
|
|
0c317d9ce1 | ||
|
|
eddfee566d | ||
|
|
7fe366a8de | ||
|
|
55be30c99f | ||
|
|
36ceef8488 | ||
|
|
b59edb5e8e | ||
|
|
5d32b6a3e7 | ||
|
|
05230ac046 | ||
|
|
f28dc08ae2 | ||
|
|
ecd958bdc5 | ||
|
|
43f59cfde8 | ||
|
|
00c46961f9 | ||
|
|
d0f1d869a8 | ||
|
|
29ee000998 | ||
|
|
995fd7fee9 | ||
|
|
67abf35a28 | ||
|
|
629beb7240 |
11
README.md
11
README.md
@@ -1,3 +1,5 @@
|
||||

|
||||
|
||||
Cambridge
|
||||
=========
|
||||
|
||||
@@ -5,12 +7,21 @@ 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
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
- [Lilla Oshisaure](https://www.youtube.com/user/LeSpyroshisaure) for their amazing contributions to my life in general!
|
||||
- [The Tetra Legends Discord](http://discord.com/invite/7hMx5r2) for supporting me and playtesting!
|
||||
- [joezeng](https://github.com/joezeng) for the original project.
|
||||
- [Hailey](https://github.com/haileylgbt) for some miscellaneous assets.
|
||||
- CylinderKnot for an amazing gamemode.
|
||||
- MarkGamed7794 for some miscellaneous contributions.
|
||||
- Mizu for the Cambridge logo and the [Cambridge launcher](https://github.com/rexxt/cambridge-launcher).
|
||||
- MattMayuga for the Cambridge banner.
|
||||
|
||||

|
||||
|
||||
Installation instructions
|
||||
-------------------------
|
||||
|
||||
@@ -61,4 +61,9 @@ function formatBigNum(number)
|
||||
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
|
||||
BIN
libs/discord-rpc.dll
Normal file
BIN
libs/discord-rpc.dll
Normal file
Binary file not shown.
BIN
libs/discord-rpc.dylib
Normal file
BIN
libs/discord-rpc.dylib
Normal file
Binary file not shown.
BIN
libs/discord-rpc.so
Normal file
BIN
libs/discord-rpc.so
Normal file
Binary file not shown.
268
libs/discordRPC.lua
Normal file
268
libs/discordRPC.lua
Normal file
@@ -0,0 +1,268 @@
|
||||
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 = false
|
||||
local bgm_locked = true
|
||||
|
||||
function switchBGM(sound, subsound)
|
||||
if bgm_locked then return end
|
||||
|
||||
@@ -20,36 +20,54 @@ 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-gears.png")
|
||||
input_config = love.graphics.newImage("res/backgrounds/options-pcb.png"),
|
||||
game_config = love.graphics.newImage("res/backgrounds/options-gears.png"),
|
||||
}
|
||||
|
||||
blocks = {
|
||||
["2tie"] = {
|
||||
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"),
|
||||
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"),
|
||||
X = love.graphics.newImage("res/img/s9.png"),
|
||||
},
|
||||
["bone"] = {
|
||||
I = love.graphics.newImage("res/img/bone.png"),
|
||||
J = love.graphics.newImage("res/img/bone.png"),
|
||||
L = love.graphics.newImage("res/img/bone.png"),
|
||||
R = love.graphics.newImage("res/img/bone.png"),
|
||||
O = 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"),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -62,4 +80,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
Normal file
58
load/rpc.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
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,6 +10,10 @@ 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)
|
||||
|
||||
32
main.lua
32
main.lua
@@ -1,6 +1,7 @@
|
||||
function love.load()
|
||||
math.randomseed(os.time())
|
||||
highscores = {}
|
||||
require "load.rpc"
|
||||
require "load.graphics"
|
||||
require "load.fonts"
|
||||
require "load.sounds"
|
||||
@@ -14,6 +15,13 @@ function love.load()
|
||||
|
||||
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()
|
||||
@@ -22,6 +30,28 @@ 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
|
||||
@@ -64,7 +94,7 @@ end
|
||||
|
||||
function love.draw()
|
||||
love.graphics.push()
|
||||
|
||||
|
||||
-- get offset matrix
|
||||
love.graphics.setDefaultFilter("linear", "nearest")
|
||||
local width = love.graphics.getWidth()
|
||||
|
||||
BIN
res/backgrounds/options-pcb.png
Normal file
BIN
res/backgrounds/options-pcb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
BIN
res/img/bonew.png
Normal file
BIN
res/img/bonew.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 B |
BIN
res/se/cursor.wav
Normal file
BIN
res/se/cursor.wav
Normal file
Binary file not shown.
BIN
res/se/cursor_lr.wav
Normal file
BIN
res/se/cursor_lr.wav
Normal file
Binary file not shown.
BIN
res/se/main_decide.wav
Normal file
BIN
res/se/main_decide.wav
Normal file
Binary file not shown.
BIN
res/se/mode_decide.wav
Normal file
BIN
res/se/mode_decide.wav
Normal file
Binary file not shown.
@@ -11,5 +11,5 @@ ExitScene = require "scene.exit"
|
||||
GameScene = require "scene.game"
|
||||
ModeSelectScene = require "scene.mode_select"
|
||||
InputConfigScene = require "scene.input_config"
|
||||
ConfigScene = require "scene.config"
|
||||
GameConfigScene = require "scene.game_config"
|
||||
TitleScene = require "scene.title"
|
||||
|
||||
@@ -5,6 +5,10 @@ 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()
|
||||
@@ -66,7 +70,7 @@ function GameScene:onKeyPress(e)
|
||||
-- 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
|
||||
-- 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)
|
||||
|
||||
79
scene/game_config.lua
Normal file
79
scene/game_config.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
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
|
||||
@@ -22,6 +22,11 @@ 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()
|
||||
@@ -34,11 +39,11 @@ function ConfigScene:render()
|
||||
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"
|
||||
@@ -62,8 +67,13 @@ function ConfigScene:onKeyPress(e)
|
||||
self.input_state = 1
|
||||
end
|
||||
else
|
||||
config.input[configurable_inputs[self.input_state]] = e.scancode
|
||||
self.input_state = self.input_state + 1
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,48 +5,16 @@ ModeSelectScene.title = "Game Start"
|
||||
current_mode = 1
|
||||
current_ruleset = 1
|
||||
|
||||
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.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_ax4',
|
||||
require 'tetris.modes.marathon_c89',
|
||||
require 'tetris.modes.survival_a1',
|
||||
require 'tetris.modes.survival_a2',
|
||||
require 'tetris.modes.survival_a3',
|
||||
require 'tetris.modes.big_a2',
|
||||
require 'tetris.modes.konoha',
|
||||
}
|
||||
|
||||
rulesets = {
|
||||
require 'tetris.rulesets.cambridge',
|
||||
require 'tetris.rulesets.arika',
|
||||
require 'tetris.rulesets.arika_ti',
|
||||
require 'tetris.rulesets.ti_srs',
|
||||
require 'tetris.rulesets.arika_ace',
|
||||
require 'tetris.rulesets.arika_srs',
|
||||
require 'tetris.rulesets.standard_exp',
|
||||
--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()
|
||||
@@ -58,31 +26,35 @@ function ModeSelectScene:render()
|
||||
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, 78 + 20 * self.menu_state.mode, 240, 22)
|
||||
love.graphics.rectangle("fill", 20, 258, 240, 22)
|
||||
|
||||
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, 78 + 20 * self.menu_state.ruleset, 200, 22)
|
||||
|
||||
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
|
||||
love.graphics.printf(mode.name, 40, 80 + 20 * idx, 200, "left")
|
||||
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
|
||||
love.graphics.printf(ruleset.name, 360, 80 + 20 * idx, 160, "left")
|
||||
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
|
||||
end
|
||||
|
||||
@@ -92,15 +64,19 @@ 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
|
||||
|
||||
@@ -3,11 +3,31 @@ 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()
|
||||
@@ -39,11 +59,14 @@ 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
|
||||
|
||||
@@ -4,6 +4,7 @@ local Grid = Object:extend()
|
||||
|
||||
local empty = { skin = "", colour = "" }
|
||||
local oob = { skin = "", colour = "" }
|
||||
local block = { skin = "2tie", colour = "X" }
|
||||
|
||||
function Grid:new()
|
||||
self.grid = {}
|
||||
@@ -141,14 +142,34 @@ 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 {
|
||||
skin = self.grid[23][col].skin,
|
||||
colour = "G"
|
||||
}
|
||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
|
||||
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)
|
||||
@@ -161,7 +182,7 @@ function Grid:applyPiece(piece)
|
||||
if y + 1 > 0 then
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.shape
|
||||
colour = piece.colour
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -177,7 +198,7 @@ function Grid:applyBigPiece(piece)
|
||||
if y*2+a > 0 then
|
||||
self.grid[y*2+a][x*2+b] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.shape
|
||||
colour = piece.colour
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -214,9 +235,9 @@ function Grid:checkSecretGrade()
|
||||
validLine = false
|
||||
end
|
||||
if(validLine) then
|
||||
sgrade = sgrade + 1
|
||||
sgrade = sgrade + 1
|
||||
else
|
||||
-- return sgrade
|
||||
return sgrade
|
||||
end
|
||||
end
|
||||
--[[
|
||||
@@ -239,7 +260,7 @@ function Grid:update()
|
||||
end
|
||||
|
||||
function Grid:draw()
|
||||
for y = 1, 24 do
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid_age[y][x] < 1 then
|
||||
@@ -271,7 +292,7 @@ function Grid:draw()
|
||||
end
|
||||
|
||||
function Grid:drawInvisible(opacity_function, garbage_opacity_function)
|
||||
for y = 1, 24 do
|
||||
for y = 5, 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, big)
|
||||
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
|
||||
self.shape = shape
|
||||
self.rotation = rotation
|
||||
self.position = position
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
@@ -21,7 +22,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.big
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||
)
|
||||
end
|
||||
|
||||
@@ -31,7 +32,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.big
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big
|
||||
)
|
||||
end
|
||||
|
||||
@@ -148,13 +149,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.shape],
|
||||
blocks[self.skin][self.colour],
|
||||
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
|
||||
0, 2, 2
|
||||
)
|
||||
else
|
||||
love.graphics.draw(
|
||||
blocks[self.skin][self.shape],
|
||||
blocks[self.skin][self.colour],
|
||||
64+x*16+partial_das, 16+y*16+gravity_offset
|
||||
)
|
||||
end
|
||||
|
||||
39
tetris/modes/4wide.lua
Normal file
39
tetris/modes/4wide.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
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
|
||||
@@ -19,13 +19,10 @@ function MarathonA2Game:new()
|
||||
self.big_mode = true
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.grade = 0
|
||||
self.grade_points = 0
|
||||
self.grade_point_decay_counter = 0
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
@@ -103,11 +100,9 @@ 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
|
||||
self.grade = 33
|
||||
end
|
||||
end
|
||||
elseif self.ready_frames == 0 then
|
||||
self.frames = self.frames + 1
|
||||
@@ -123,42 +118,29 @@ end
|
||||
|
||||
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||
cleared_row_count = cleared_row_count / 2
|
||||
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()
|
||||
self.roll_frames = -150
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
cleared_lines = cleared_lines / 2
|
||||
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
|
||||
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
|
||||
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
|
||||
new_level >= 999 then
|
||||
-- record new section
|
||||
section_time = self.frames - self.section_start_time
|
||||
self.section_times[math.floor(old_level / 100)] = section_time
|
||||
self.section_start_time = self.frames
|
||||
end
|
||||
end
|
||||
|
||||
@@ -223,7 +205,7 @@ local grade_conversion = {
|
||||
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, 18, 19
|
||||
17
|
||||
}
|
||||
|
||||
function MarathonA2Game:updateGrade(cleared_lines)
|
||||
@@ -248,49 +230,12 @@ function MarathonA2Game:updateGrade(cleared_lines)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
-- tetris requirements
|
||||
for section = 0, 9 do
|
||||
if self.section_tetrises[section] < tetris_requirements[section] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- section time requirements
|
||||
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
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- section time average requirements
|
||||
if self.section_times[5] > section_average / 5 then
|
||||
return false
|
||||
end
|
||||
for section = 6, 9 do
|
||||
if self.section_times[section] > self.section_times[section - 1] + 120 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.grade < 17 or self.frames > frameTime(8,45) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
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)
|
||||
elseif grade == 18 then
|
||||
return "M"
|
||||
else
|
||||
return "GM"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -300,18 +245,9 @@ MarathonA2Game.rollOpacityFunction = function(age)
|
||||
else return 1 - (age - 240) / 60 end
|
||||
end
|
||||
|
||||
MarathonA2Game.mRollOpacityFunction = function(age)
|
||||
if age > 4 then return 0
|
||||
else return 1 - age / 4 end
|
||||
end
|
||||
|
||||
function MarathonA2Game:drawGrid(ruleset)
|
||||
if self.clear and not (self.completed or self.game_over) then
|
||||
if self:qualifiesForMRoll() then
|
||||
self.grid:drawInvisible(self.mRollOpacityFunction)
|
||||
else
|
||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
||||
end
|
||||
self.grid:drawInvisible(self.rollOpacityFunction)
|
||||
else
|
||||
self.grid:draw()
|
||||
if self.piece ~= nil and self.level < 100 then
|
||||
@@ -335,7 +271,10 @@ function MarathonA2Game:drawScoringInfo()
|
||||
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")
|
||||
|
||||
326
tetris/modes/ck.lua
Normal file
326
tetris/modes/ck.lua
Normal file
@@ -0,0 +1,326 @@
|
||||
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
|
||||
@@ -11,9 +11,6 @@ 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
|
||||
@@ -29,6 +26,9 @@ 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()
|
||||
@@ -166,11 +166,20 @@ function DemonModeGame:updateSectionTimes(old_level, new_level)
|
||||
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)
|
||||
@@ -178,16 +187,17 @@ function DemonModeGame:updateSectionTimes(old_level, new_level)
|
||||
end
|
||||
|
||||
function DemonModeGame:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -229,7 +239,7 @@ function DemonModeGame:drawScoringInfo()
|
||||
strTrueValues(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", 240, 120, 40, "left")
|
||||
if self.grade ~= 0 then love.graphics.printf("GRADE", 240, 120, 40, "left") end
|
||||
love.graphics.printf("SCORE", 240, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
@@ -252,7 +262,7 @@ function DemonModeGame:getHighscoreData()
|
||||
end
|
||||
|
||||
function DemonModeGame:getBackground()
|
||||
return math.floor(self.level / 100)
|
||||
return math.min(math.floor(self.level / 100), 19)
|
||||
end
|
||||
|
||||
return DemonModeGame
|
||||
|
||||
@@ -40,9 +40,12 @@ 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
|
||||
@@ -59,6 +62,7 @@ function GameMode:getLineClearDelay() return 40 end
|
||||
function GameMode:getDasLimit() return 15 end
|
||||
|
||||
function GameMode:getNextPiece(ruleset)
|
||||
|
||||
return {
|
||||
skin = "2tie",
|
||||
shape = self.randomizer:nextPiece(),
|
||||
@@ -72,6 +76,8 @@ function GameMode:initialize(ruleset)
|
||||
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)
|
||||
@@ -125,7 +131,7 @@ function GameMode:update(inputs, ruleset)
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
not self.hard_drop_locked then
|
||||
self:onHardDrop(piece_dy)
|
||||
if self.instant_hard_drop then
|
||||
if self.lock_on_hard_drop then
|
||||
self.piece.locked = true
|
||||
end
|
||||
end
|
||||
@@ -134,7 +140,7 @@ function GameMode:update(inputs, ruleset)
|
||||
self:onSoftDrop(piece_dy)
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
not self.drop_locked and
|
||||
self.instant_soft_drop
|
||||
self.lock_on_soft_drop
|
||||
then
|
||||
self.piece.locked = true
|
||||
end
|
||||
@@ -340,11 +346,12 @@ 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][piece], pos_x+x*16, pos_y+y*16)
|
||||
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
|
||||
end
|
||||
end
|
||||
for i = 1, self.next_queue_length do
|
||||
@@ -359,7 +366,8 @@ function GameMode:drawNextQueue(ruleset)
|
||||
end
|
||||
end
|
||||
if self.hold_queue ~= nil then
|
||||
self:setHoldOpacity()
|
||||
local hold_color = self.held and 0.6 or 1
|
||||
self:setHoldOpacity(1, hold_color)
|
||||
drawPiece(
|
||||
self.hold_queue.shape,
|
||||
self.hold_queue.skin,
|
||||
@@ -370,8 +378,16 @@ function GameMode:drawNextQueue(ruleset)
|
||||
return false
|
||||
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: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:drawScoringInfo()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -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'
|
||||
local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'
|
||||
|
||||
local IntervalTrainingGame = GameMode:extend()
|
||||
|
||||
@@ -15,11 +15,12 @@ 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
|
||||
@@ -27,20 +28,26 @@ 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 4
|
||||
return 6
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:getLineARE()
|
||||
return 4
|
||||
return 6
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:getDasLimit()
|
||||
return 6
|
||||
return 7
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:getLineClearDelay()
|
||||
return 6
|
||||
return 4
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:getLockDelay()
|
||||
@@ -61,7 +68,6 @@ 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
|
||||
@@ -72,14 +78,15 @@ function IntervalTrainingGame:advanceOneFrame()
|
||||
end
|
||||
|
||||
function IntervalTrainingGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
||||
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 IntervalTrainingGame:onLineClear(cleared_row_count)
|
||||
local cleared_level_bonus = {1, 2, 4, 6}
|
||||
if not self.clear then
|
||||
local new_level = self.level + cleared_row_count
|
||||
local new_level = self.level + cleared_level_bonus[cleared_row_count]
|
||||
self:updateSectionTimes(self.level, new_level)
|
||||
self.level = math.min(new_level, 999)
|
||||
if self.level == 999 then
|
||||
@@ -97,8 +104,6 @@ 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
|
||||
|
||||
@@ -123,7 +128,7 @@ function IntervalTrainingGame:drawScoringInfo()
|
||||
strTrueValues(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("TIME LEFT", 240, 250, 80, "left")
|
||||
if not self.clear then love.graphics.printf("TIME LEFT", 240, 250, 80, "left") end
|
||||
love.graphics.printf("LEVEL", 240, 320, 40, "left")
|
||||
|
||||
local current_section = math.floor(self.level / 100) + 1
|
||||
@@ -134,10 +139,10 @@ function IntervalTrainingGame:drawScoringInfo()
|
||||
|
||||
-- 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
|
||||
if not self.game_over 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")
|
||||
if not self.clear then love.graphics.printf(formatTime(time_left), 240, 270, 160, "left") end
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right")
|
||||
|
||||
@@ -36,6 +36,8 @@ 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()
|
||||
@@ -327,6 +329,7 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
|
||||
self.section_cool_count = self.section_cool_count + 1
|
||||
self.delay_level = math.min(20, self.delay_level + 1)
|
||||
table.insert(self.section_status, "cool")
|
||||
self.cool_timer = 300
|
||||
end
|
||||
|
||||
local section = getSectionForLevel(old_level)
|
||||
@@ -430,6 +433,11 @@ function Marathon2020Game:drawScoringInfo()
|
||||
|
||||
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
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self:getTotalGrade(), text_x, 120, 90, "left")
|
||||
love.graphics.printf(self.grade_points, text_x, 220, 90, "left")
|
||||
|
||||
@@ -18,6 +18,7 @@ function MarathonA1Game:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.bravos = 0
|
||||
self.gm_conditions = {
|
||||
level300 = false,
|
||||
level500 = false,
|
||||
@@ -136,40 +137,44 @@ function MarathonA1Game: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 self.level == 999 then
|
||||
if new_level == 999 then
|
||||
self.clear = true
|
||||
else
|
||||
self.level = new_level
|
||||
end
|
||||
self.level = new_level
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA1Game: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
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
else
|
||||
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
|
||||
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 <= frameTime(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 <= frameTime(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["level900"] = true
|
||||
if self.score >= 126000 and self.frames <= frameTime(13,30) then
|
||||
self.gm_conditions["level999"] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -201,9 +206,11 @@ function MarathonA1Game:drawScoringInfo()
|
||||
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["level900"] then
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||
@@ -214,6 +221,7 @@ function MarathonA1Game:drawScoringInfo()
|
||||
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")
|
||||
|
||||
@@ -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()
|
||||
@@ -32,8 +32,6 @@ function MarathonA2Game:new()
|
||||
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
|
||||
self.lock_drop = false
|
||||
self.enable_hold = false
|
||||
@@ -109,6 +107,7 @@ 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
|
||||
@@ -127,34 +126,33 @@ 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
|
||||
@@ -252,7 +250,7 @@ function MarathonA2Game:updateGrade(cleared_lines)
|
||||
end
|
||||
end
|
||||
|
||||
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }
|
||||
local tetris_requirements = { [0] = 2, 2, 2, 2, 2, 1, 1, 1, 1, 0 }
|
||||
|
||||
function MarathonA2Game:qualifiesForMRoll()
|
||||
if not self.clear then return false end
|
||||
@@ -279,7 +277,7 @@ function MarathonA2Game:qualifiesForMRoll()
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.grade < 17 or self.frames > frameTime(9,30) then
|
||||
if self.grade < 31 or self.frames > frameTime(8,45) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
@@ -343,7 +341,17 @@ function MarathonA2Game:drawScoringInfo()
|
||||
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")
|
||||
|
||||
@@ -45,6 +45,8 @@ self.SGnames = {
|
||||
|
||||
self.coolregret_message = "COOL!!"
|
||||
self.coolregret_timer = 0
|
||||
|
||||
self.torikan_passed = false
|
||||
end
|
||||
|
||||
function MarathonA3Game:getARE()
|
||||
@@ -166,6 +168,10 @@ 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
|
||||
elseif self.level >= 500 then self.torikan_passed = true end
|
||||
end
|
||||
|
||||
local cool_cutoffs = {
|
||||
@@ -219,17 +225,18 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
|
||||
end
|
||||
|
||||
function MarathonA3Game: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
|
||||
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.drop_bonus = 0
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ function MarathonAX4Game:onLineClear(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
|
||||
|
||||
@@ -16,6 +16,12 @@ 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
|
||||
@@ -111,16 +117,17 @@ function PhantomManiaGame:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function PhantomManiaGame:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -161,12 +168,16 @@ function PhantomManiaGame:drawScoringInfo()
|
||||
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")
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||
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(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left")
|
||||
if getLetterGrade(self.level, self.clear) ~= "" then love.graphics.printf(getLetterGrade(self.level, self.clear), text_x, 140, 90, "left") end
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
if self.clear then
|
||||
@@ -175,6 +186,9 @@ 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,7 +16,6 @@ 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
|
||||
@@ -26,12 +25,21 @@ 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()
|
||||
@@ -173,16 +181,17 @@ function PhantomMania2Game:onHold()
|
||||
end
|
||||
|
||||
function PhantomMania2Game:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -207,8 +216,13 @@ 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
|
||||
@@ -285,6 +299,15 @@ 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")
|
||||
@@ -295,6 +318,10 @@ 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,6 +8,12 @@ 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 History6RollsRandomizer = require 'tetris.randomizers.history_6rolls'
|
||||
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
|
||||
|
||||
local Race40Game = GameMode:extend()
|
||||
|
||||
@@ -18,9 +18,17 @@ function Race40Game:new()
|
||||
self.lines = 0
|
||||
self.line_goal = 40
|
||||
self.pieces = 0
|
||||
self.randomizer = History6RollsRandomizer()
|
||||
self.randomizer = Bag7Randomiser()
|
||||
|
||||
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
|
||||
@@ -35,7 +43,7 @@ function Race40Game:getDropSpeed()
|
||||
end
|
||||
|
||||
function Race40Game:getARR()
|
||||
return 0
|
||||
return 1
|
||||
end
|
||||
|
||||
function Race40Game:getARE()
|
||||
@@ -47,7 +55,7 @@ function Race40Game:getLineARE()
|
||||
end
|
||||
|
||||
function Race40Game:getDasLimit()
|
||||
return 6
|
||||
return 10
|
||||
end
|
||||
|
||||
function Race40Game:getLineClearDelay()
|
||||
@@ -55,7 +63,7 @@ function Race40Game:getLineClearDelay()
|
||||
end
|
||||
|
||||
function Race40Game:getLockDelay()
|
||||
return 15
|
||||
return 30
|
||||
end
|
||||
|
||||
function Race40Game:getGravity()
|
||||
@@ -102,6 +110,12 @@ 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)
|
||||
@@ -113,10 +127,17 @@ 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")
|
||||
|
||||
@@ -16,7 +16,6 @@ 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
|
||||
@@ -84,7 +83,7 @@ function StrategyGame:advanceOneFrame()
|
||||
end
|
||||
|
||||
function StrategyGame:onPieceEnter()
|
||||
if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
|
||||
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
|
||||
@@ -99,16 +98,17 @@ function StrategyGame:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function StrategyGame:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
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, 50, "right")
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
if self.clear then
|
||||
love.graphics.printf(self.level, text_x, 370, 50, "right")
|
||||
love.graphics.printf(self.level, text_x, 370, 40, "right")
|
||||
else
|
||||
love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
|
||||
love.graphics.printf(self.level < 900 and math.floor(self.level / 100 + 1) * 100 or 999, text_x, 370, 40, "right")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -174,16 +174,17 @@ function Survival2020Game:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function Survival2020Game:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ function SurvivalA1Game:new()
|
||||
|
||||
self.roll_frames = 0
|
||||
self.combo = 1
|
||||
self.bravos = 0
|
||||
|
||||
self.gm_conditions = {
|
||||
level300 = false,
|
||||
level500 = false,
|
||||
@@ -107,40 +109,45 @@ 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 self.level == 999 then
|
||||
if new_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 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
|
||||
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
|
||||
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 <= frameTime(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 <= frameTime(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["level900"] = true
|
||||
if self.score >= 126000 and self.frames <= frameTime(13,30) then
|
||||
self.gm_conditions["level999"] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -169,9 +176,11 @@ function SurvivalA1Game:drawScoringInfo()
|
||||
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["level900"] then
|
||||
if self.gm_conditions["level300"] and self.gm_conditions["level500"] and self.gm_conditions["level999"] then
|
||||
love.graphics.printf("GM", 240, 140, 90, "left")
|
||||
else
|
||||
love.graphics.printf(getRankForScore(self.score).rank, 240, 140, 90, "left")
|
||||
@@ -182,6 +191,7 @@ function SurvivalA1Game:drawScoringInfo()
|
||||
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")
|
||||
|
||||
@@ -88,7 +88,7 @@ function SurvivalA2Game:advanceOneFrame()
|
||||
end
|
||||
|
||||
function SurvivalA2Game:onPieceEnter()
|
||||
if (self.level % 100 ~= 99 or self.level == 998) and not self.clear and self.frames ~= 0 then
|
||||
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
|
||||
@@ -98,6 +98,9 @@ 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
|
||||
@@ -105,22 +108,25 @@ function SurvivalA2Game:onLineClear(cleared_row_count)
|
||||
end
|
||||
|
||||
function SurvivalA2Game:updateScore(level, drop_bonus, 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
|
||||
)
|
||||
self.lines = self.lines + cleared_lines
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
else
|
||||
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
|
||||
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 then return "M"
|
||||
elseif self.level == 500 and not self.clear then return "M"
|
||||
else return "" end
|
||||
end
|
||||
|
||||
@@ -141,7 +147,7 @@ function SurvivalA2Game:drawScoringInfo()
|
||||
strTrueValues(self.prev_inputs)
|
||||
)
|
||||
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
||||
love.graphics.printf("GRADE", text_x, 120, 40, "left")
|
||||
if self:getLetterGrade() ~= "" then love.graphics.printf("GRADE", text_x, 120, 40, "left") end
|
||||
love.graphics.printf("SCORE", text_x, 200, 40, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
local sg = self.grid:checkSecretGrade()
|
||||
@@ -151,7 +157,7 @@ function SurvivalA2Game:drawScoringInfo()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.printf(self.score, text_x, 220, 90, "left")
|
||||
love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left")
|
||||
if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
|
||||
love.graphics.printf(self.level, text_x, 340, 40, "right")
|
||||
love.graphics.printf(self:getSectionEndLevel(), text_x, 370, 40, "right")
|
||||
if sg >= 5 then
|
||||
|
||||
@@ -16,7 +16,6 @@ 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
|
||||
@@ -30,7 +29,7 @@ function SurvivalA3Game:new()
|
||||
"m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9",
|
||||
"GM"
|
||||
}
|
||||
|
||||
|
||||
self.lock_drop = true
|
||||
self.enable_hold = true
|
||||
self.next_queue_length = 3
|
||||
@@ -39,6 +38,14 @@ function SurvivalA3Game:new()
|
||||
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()
|
||||
if self.level < 300 then return 12
|
||||
else return 6 end
|
||||
@@ -95,11 +102,11 @@ function SurvivalA3Game:getNextPiece(ruleset)
|
||||
end
|
||||
|
||||
function SurvivalA3Game:hitTorikan(old_level, new_level)
|
||||
if old_level < 500 and new_level >= 500 and self.frames > frameTime(2,28) then
|
||||
if old_level < 500 and new_level >= 500 and self.frames > self.torikan_time then
|
||||
self.level = 500
|
||||
return true
|
||||
end
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(4,56) then
|
||||
if old_level < 1000 and new_level >= 1000 and self.frames > self.torikan_time*2 then
|
||||
self.level = 1000
|
||||
return true
|
||||
end
|
||||
@@ -132,20 +139,21 @@ 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
|
||||
end
|
||||
self.clear = true
|
||||
self.grid:clear()
|
||||
self.big_mode = true
|
||||
self.roll_frames = -150
|
||||
else
|
||||
self.level = math.min(new_level, 1300)
|
||||
end
|
||||
@@ -158,16 +166,17 @@ function SurvivalA3Game:onPieceLock(piece, cleared_row_count)
|
||||
end
|
||||
|
||||
function SurvivalA3Game:updateScore(level, drop_bonus, 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
|
||||
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
|
||||
self.combo = 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -233,7 +242,10 @@ function SurvivalA3Game:drawScoringInfo()
|
||||
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
|
||||
|
||||
209
tetris/modes/tgmplus.lua
Normal file
209
tetris/modes/tgmplus.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
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
|
||||
@@ -50,9 +50,6 @@ 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
|
||||
@@ -2,17 +2,8 @@ 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 self.piece
|
||||
return "I"
|
||||
end
|
||||
|
||||
return AlwaysRandomizer
|
||||
|
||||
24
tetris/randomizers/bag8.lua
Normal file
24
tetris/randomizers/bag8.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
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
|
||||
30
tetris/randomizers/recursive_bag.lua
Normal file
30
tetris/randomizers/recursive_bag.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
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
|
||||
19
tetris/randomizers/sega.lua
Normal file
19
tetris/randomizers/sega.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
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=2, y=2 },
|
||||
I = { x=3, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
@@ -110,7 +110,7 @@ function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -6,6 +6,19 @@ local ARS = Ruleset: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 },
|
||||
@@ -17,10 +30,10 @@ ARS.spawn_positions = {
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=2, y=0 },
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
@@ -131,7 +144,7 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
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 == 1
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
@@ -172,7 +185,7 @@ function ARS:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
178
tetris/rulesets/arika_ace2.lua
Normal file
178
tetris/rulesets/arika_ace2.lua
Normal file
@@ -0,0 +1,178 @@
|
||||
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
|
||||
@@ -5,6 +5,18 @@ 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.enable_IRS_wallkicks = true
|
||||
|
||||
@@ -19,10 +31,10 @@ SRS.spawn_positions = {
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=2, y=0 },
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
@@ -98,24 +110,24 @@ SRS.wallkicks_3x3 = {
|
||||
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
[2]={},
|
||||
[3]={{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=-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}},
|
||||
[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=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}},
|
||||
[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=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}},
|
||||
[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}},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -174,4 +186,5 @@ function SRS:onPieceRotate(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 3 end
|
||||
return SRS
|
||||
|
||||
@@ -17,10 +17,10 @@ ARS.spawn_positions = {
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=2, y=2 },
|
||||
I = { x=3, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=3 },
|
||||
@@ -131,7 +131,7 @@ function ARS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
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 == 1
|
||||
and new_piece.rotation == 0
|
||||
and piece.floorkick == 0
|
||||
and grid:canPlacePiece(new_piece:withOffset({x=0, y=-1}))
|
||||
then
|
||||
@@ -151,7 +151,7 @@ function ARS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
end
|
||||
|
||||
function ARS:get180RotationValue() return config["reverse_rotate"] and 1 or 3 end
|
||||
function ARS:get180RotationValue() return 3 end
|
||||
function ARS:getDefaultOrientation() return 3 end -- downward facing pieces by default
|
||||
|
||||
return ARS
|
||||
|
||||
@@ -5,25 +5,26 @@ 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=4 },
|
||||
S = { x=4, y=5 },
|
||||
T = { x=4, y=5 },
|
||||
Z = { x=4, y=4 },
|
||||
Z = { x=4, y=5 },
|
||||
}
|
||||
|
||||
CRS.big_spawn_positions = {
|
||||
I = { x=2, y=2 },
|
||||
I = { x=3, y=2 },
|
||||
J = { x=2, y=3 },
|
||||
L = { x=2, y=3 },
|
||||
O = { x=2, y=3 },
|
||||
S = { x=2, y=2 },
|
||||
O = { x=3, y=3 },
|
||||
S = { x=2, y=3 },
|
||||
T = { x=2, y=3 },
|
||||
Z = { x=2, y=2 },
|
||||
Z = { x=2, y=3 },
|
||||
}
|
||||
|
||||
CRS.block_offsets = {
|
||||
@@ -52,10 +53,10 @@ CRS.block_offsets = {
|
||||
{ {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=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} },
|
||||
{ {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} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
||||
@@ -64,10 +65,10 @@ CRS.block_offsets = {
|
||||
{ {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} },
|
||||
{ {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} },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,6 +363,10 @@ 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)
|
||||
|
||||
@@ -6,10 +6,47 @@ 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 = {}
|
||||
|
||||
@@ -39,6 +76,9 @@ 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)
|
||||
|
||||
@@ -117,10 +157,12 @@ 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, big)
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
|
||||
@@ -5,6 +5,18 @@ 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
|
||||
|
||||
@@ -19,10 +31,10 @@ SRS.spawn_positions = {
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=2, y=0 },
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
@@ -119,6 +131,16 @@ SRS.wallkicks_line = {
|
||||
},
|
||||
};
|
||||
|
||||
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)
|
||||
@@ -147,19 +169,24 @@ function SRS:attemptWallkicks(piece, new_piece, rot_dir, grid)
|
||||
end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.rotate_counter = 0
|
||||
piece.move_counter = 0
|
||||
piece.manipulations = 0
|
||||
piece.lowest_y = -math.huge
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
piece.lock_delay = 0 -- step reset
|
||||
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.move_counter = piece.move_counter + 1
|
||||
if piece.move_counter >= 24 then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 15 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
@@ -167,9 +194,10 @@ 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
|
||||
piece.rotate_counter = piece.rotate_counter + 1
|
||||
if piece.rotate_counter >= 12 then
|
||||
if piece.manipulations >= 15 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,27 +5,39 @@ 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.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 },
|
||||
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 },
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=2, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=2, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
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 },
|
||||
}
|
||||
|
||||
SRS.block_offsets = {
|
||||
@@ -98,24 +110,24 @@ SRS.wallkicks_3x3 = {
|
||||
|
||||
SRS.wallkicks_line = {
|
||||
[0]={
|
||||
[1]={{x=-2, y=0}, {x=1, y=0}, {x=-2, y=1}, {x=1, y=-2}},
|
||||
[1]={{x=-2, y= 0}, {x= 1, y= 0}, {x= 1, y=-2}, {x=-2, y= 1}},
|
||||
[2]={},
|
||||
[3]={{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=-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}},
|
||||
[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=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}},
|
||||
[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=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}},
|
||||
[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}},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -148,6 +160,7 @@ end
|
||||
|
||||
function SRS:onPieceCreate(piece, grid)
|
||||
piece.manipulations = 0
|
||||
piece.rotations = 0
|
||||
end
|
||||
|
||||
function SRS:onPieceDrop(piece, grid)
|
||||
@@ -158,7 +171,7 @@ function SRS:onPieceMove(piece, grid)
|
||||
piece.lock_delay = 0 -- move reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
piece.manipulations = piece.manipulations + 1
|
||||
if piece.manipulations >= 8 then
|
||||
if piece.manipulations >= 10 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
@@ -167,11 +180,12 @@ 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 >= 8 then
|
||||
piece.rotations = piece.rotations + 1
|
||||
if piece.rotations >= 8 then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SRS:get180RotationValue() return 3 end
|
||||
return SRS
|
||||
|
||||
Reference in New Issue
Block a user