Compare commits

..

91 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
062ab2005e v0.2 release commit - hold piece darken 2020-11-03 16:56:08 -05:00
Ishaan Bhardwaj
468025fc80 last commit to core modes before release 2020-11-03 12:17:36 -05:00
Ishaan Bhardwaj
c8544975d6 Fix interval training 2020-11-03 11:55:30 -05:00
Ishaan Bhardwaj
6776229bfb Small push to Cambridge modes 2020-11-03 11:52:52 -05:00
Ishaan Bhardwaj
84b4dc5073 World Bone Blocks 2020-11-03 10:58:21 -05:00
Ishaan Bhardwaj
35dafb6615 keep leaving debug code in new commits 2020-11-02 22:51:16 -05:00
Ishaan Bhardwaj
3641d85fcb Major changes, including modpack support 2020-11-02 22:47:58 -05:00
Ishaan Bhardwaj
9b89c4d1de G/O line fix 2020-11-02 21:17:13 -05:00
Ishaan Bhardwaj
2dba120919 Green line and orange line for TAP Master 2020-11-02 20:43:10 -05:00
Ishaan Bhardwaj
9224f271b1 Hotfix for last 2020-11-02 16:20:22 -05:00
Ishaan Bhardwaj
febb5d546c Score overhauls 2020-11-02 16:12:05 -05:00
Ishaan Bhardwaj
c6482c423e 4w optimization and green/orange line adding for applicable modes 2020-11-02 13:46:16 -05:00
Ishaan Bhardwaj
6beb313c6b Debug fixes 2020-11-02 12:44:15 -05:00
Ishaan Bhardwaj
eb70f55b6e TGM2 fixes and cool fixes 2020-11-02 12:21:12 -05:00
Ishaan Bhardwaj
0badcde9ad Basset: the only person to leave debug code in a repo 2020-11-01 13:44:35 -05:00
Ishaan Bhardwaj
6f39b591d3 Hotfix for TGM+ 2020-11-01 13:28:13 -05:00
Ishaan Bhardwaj
129237f0b0 TGM+ 2020-11-01 13:24:52 -05:00
Ishaan Bhardwaj
741c246244 second lol 2020-11-01 12:04:07 -05:00
Ishaan Bhardwaj
b5937af8b2 lol 2020-11-01 12:01:26 -05:00
Ishaan Bhardwaj
33b8533d8e Fixes to TAP M-roll requirements 2020-11-01 11:06:43 -05:00
Ishaan Bhardwaj
69959ff687 TA Death level advance formula is very bugged 2020-10-30 21:36:05 -04:00
Ishaan Bhardwaj
f91cd99dfd Minor fixes to TGM modes 2020-10-30 21:28:39 -04:00
Ishaan Bhardwaj
be59727ca5 Some demon mode fixes 2020-10-30 13:09:49 -04:00
Ishaan Bhardwaj
cca295066c Fix. 2020-10-29 23:05:49 -04:00
Ishaan Bhardwaj
f2862b4d93 Some score fixes for core TGM modes 2020-10-29 23:03:54 -04:00
Ishaan Bhardwaj
2aafd30253 Fixed secret grade detection 2020-10-29 22:14:34 -04:00
Ishaan Bhardwaj
b27ba335ba Improved secret grade 2020-10-29 21:40:50 -04:00
Ishaan Bhardwaj
33244736b8 Secret grade for sprint? Also ARR and DAS change so remember to change it in the lua file 2020-10-29 20:56:18 -04:00
Ishaan Bhardwaj
285108ca08 ACTUALLY fixed TI Master torikan 2020-10-26 14:07:09 -04:00
Ishaan Bhardwaj
4b1fed727c Update marathon_a3.lua 2020-10-26 14:03:09 -04:00
Ishaan Bhardwaj
9fca272e8d Update ck.lua 2020-10-24 09:12:12 -04:00
Ishaan Bhardwaj
5a21c8244b Update README.md 2020-10-23 21:28:32 -04:00
Ishaan Bhardwaj
4923b2e2d4 Removed debug code 2020-10-23 21:23:45 -04:00
Ishaan Bhardwaj
8810f24e7a v0.1.8, SHIRASE-CK ADDEDgit add scene/mode_select.lua tetris/modes/ck.lua ! 2020-10-23 21:02:27 -04:00
Ishaan Bhardwaj
57a9f6ef55 Fixing Demon Mode backgrounds 2020-10-23 14:52:59 -04:00
Oshisaure
342036bc28 Fixed guideline SRS anti-stalling to work closer to guideline games
Currenly behaves similarly to Tetris Friends, lock as soon as it can after exceeding the manipulation limit.
That still allows to have a piece in the air forever in low G though, might be worth looking into it?
2020-10-21 05:30:28 +01:00
Oshisaure
78dcfe43c4 Made Survival A2/A3 torikans stop your game instead of giving an end roll 2020-10-21 05:24:11 +01:00
Ishaan Bhardwaj
cdf6b5cf33 TI Master torikan 2020-10-20 23:14:25 -04:00
Oshisaure
e6a60b0021 Merge branch 'master' of https://github.com/SashLilac/cambridge into master 2020-10-19 16:54:27 +01:00
Oshisaure
5f29c987f2 Tweaked Cambridge RS to fix how S and Z show in the next queue 2020-10-19 16:50:49 +01:00
Ishaan Bhardwaj
608d75b1ac Update README.md 2020-10-19 08:45:22 -04:00
Oshisaure
1427c0d19e Updated Ti-World and ACE-SRS to use the Arika kick table
This possibly needs further testing, although the example
given [on the wiki](tetris.wiki/SRS#Arika_SRS) works
2020-10-19 05:07:31 +01:00
Ishaan Bhardwaj
e221a91d73 Swapped some settings around 2020-10-18 23:32:57 -04:00
Oshisaure
bdcd25b82c Fixed T floorkick in ARS Ti/ACE/ACE2 2020-10-19 04:19:36 +01:00
Ishaan Bhardwaj
a5158e0994 Fixed Demon Mode torikan madness 2020-10-18 21:54:24 -04:00
Oshisaure
d946b17e13 Minor change on PR #3 to use the error handling that was already implemented 2020-10-18 00:40:14 +01:00
Oshisaure
69a5c0a21a Merge pull request #3 from MyPasswordIsWeak/master
Fixed discord rpc not working on linux
2020-10-18 00:12:50 +01:00
MyPasswordIsWeak
b6423c3335 Change tabs to spaces for consistency 2020-10-17 21:17:49 +02:00
MyPasswordIsWeak
5b960d7291 Change single quotes to double quotes 2020-10-17 21:15:06 +02:00
MyPasswordIsWeak
54f4b0b890 Fixed rpc not working on linux 2020-10-17 21:11:38 +02:00
Oshisaure
8c62f321a0 Added secret grade for the Phantom Mania/-2/-N modes 2020-10-17 04:22:41 +01:00
Oshisaure
fdffd2cd9a Actually made ACE-ARS2 selectable 2020-10-17 04:17:25 +01:00
Oshisaure
8ddf468121 Removed print statements leftover from when i was making sure the torikans were working as intended 2020-10-17 03:42:31 +01:00
Oshisaure
8e77407ff2 Merge branch 'master' of https://github.com/SashLilac/cambridge 2020-10-17 03:38:07 +01:00
Oshisaure
92c852d178 Renamed ACE-ARS to ACE-ARS2 and added ACE-ARS that guideline piece lock and colours 2020-10-17 03:37:44 +01:00
Ishaan Bhardwaj
f658ed63f2 apparently I made ti-srs too strict 2020-10-16 22:20:18 -04:00
Oshisaure
c2d1c1183c Survival A3 torikan now switches to 03:03:00 when playing with World-type rulesets 2020-10-17 02:51:29 +01:00
Ishaan Bhardwaj
36c568feaf demon mode grade display fixed 2020-10-16 21:35:30 -04:00
Ishaan Bhardwaj
bf30fcefbd Add Sega randomizer 2020-10-14 15:00:17 -04:00
Oshisaure
d9f5bd16d7 Disabled BGM for now since the only music is the pacer test (not selectable in game) and the credit roll 2020-10-14 19:30:37 +01:00
Ishaan Bhardwaj
d978ff8d87 fixed a thing 2020-10-14 13:45:56 -04:00
Ishaan Bhardwaj
b47d0f36b9 oshi forced me to add bags 2020-10-14 13:43:28 -04:00
Ishaan Bhardwaj
abc210c69c made our debug randomizer not bad 2020-10-13 22:10:27 -04:00
Ishaan Bhardwaj
436e4ac861 Update README.md 2020-10-13 15:56:11 -04:00
Oshisaure
a48d7c67b5 Merge branch 'HEAD' of https://github.com/SashLilac/cambridge.git 2020-10-13 20:26:33 +01:00
Oshisaure
f6ca79ff91 Fixed big mode spawn positions 2020-10-13 20:26:07 +01:00
Ishaan Bhardwaj
4eb3901610 Some big mode fixes 2020-10-13 14:16:23 -04:00
Ishaan Bhardwaj
3f7fc4b622 Rename Mac RPC lib 2020-10-13 11:20:46 -04:00
Ishaan Bhardwaj
ac7ae91c39 Small RPC change 2020-10-12 22:44:47 -04:00
Oshisaure
0c8e910245 Linux RPC maybe? 2020-10-13 01:23:24 +01:00
Oshisaure
6233ffb12d Fixed RPC icon and included RPC lib for mac 2020-10-13 00:39:13 +01:00
Oshisaure
1f78bb9e99 Merge branch 'HEAD' of https://github.com/SashLilac/cambridge.git 2020-10-12 21:22:15 +01:00
Oshisaure
a125c09106 Fixed crash on loading the game with no save 2020-10-12 21:21:10 +01:00
Ishaan Bhardwaj
090ffa5126 Update README.md 2020-10-12 14:48:00 -04:00
Ishaan Bhardwaj
12a6f42198 Revert "Fixing step reset" - didn't realize infinite floorkicks
This reverts commit 0c317d9ce1.
2020-10-11 15:46:34 -04:00
Ishaan Bhardwaj
0c317d9ce1 Fixing step reset 2020-10-11 15:41:56 -04:00
Ishaan Bhardwaj
eddfee566d Grade display changed for TA Death modes 2020-10-11 15:34:10 -04:00
Ishaan Bhardwaj
7fe366a8de Bravo score update 2020-10-11 14:17:18 -04:00
Ishaan Bhardwaj
55be30c99f experimental bravo formula for tgm1 2020-10-11 13:21:03 -04:00
Ishaan Bhardwaj
36ceef8488 experimental bravo formula for tgm1 2020-10-11 12:57:57 -04:00
Oshisaure
b59edb5e8e Accidentally swapped blue and orange in the colour scheme update, changing it back with this commit 2020-10-11 03:12:22 +01:00
Oshisaure
5d32b6a3e7 Discord RPC cleanup
- Loading Discord RPC is now handled by `load/rpc.lua`
- Removed `presence` global, call `DiscordRPC:update()` directly with what needs updating
- Game doesn't crash anymore if the Discord RPC fails to load
- Added RPC variables in the gamemode superclass to let each gamemode handle its special case
2020-10-11 02:17:48 +01:00
Oshisaure
05230ac046 Game settings screen, and minor fix on discordRPC
- Uses BG previously from the input config screen, which has gotten a new BG
- Minor tweak on the input config screen to display all inputs names regardless of if they are bound or not
- Added Mod1 function to `funcs.lua`, may be useful again sometime
- Added game settings
  * Manual locking (per gamemode, per ruleset, on harddrop or on softdrop)
  * Piece colours (per ruleset, TTC or Arika)
  * World Reverse toggle
- Moved the discordRPC `libs/` directory, as it's a third party library
- Edited the `discordRPC.lua` file to look for the dll at the right place regardless of how you run the game (until we fuse it that is)

This should have probably been done in several commits, sorry about that
2020-10-11 00:42:56 +01:00
Oshisaure
f28dc08ae2 Updated Race 40 randomiser to use 7-bag no SZO start 2020-10-10 23:07:12 +01:00
Ishaan Bhardwaj
ecd958bdc5 Update README.md 2020-10-10 10:30:31 -04:00
Ishaan Bhardwaj
43f59cfde8 Merge pull request #1 from haileylgbt/master
Added fitting menu sfx + RPC
2020-10-09 21:37:45 -04:00
Hailey
d0f1d869a8 RP Icon 2020-10-10 09:47:33 +10:00
Hailey
29ee000998 Minor Rich Presence adjustments 2020-10-10 09:05:59 +10:00
Hailey
995fd7fee9 rich presence!! 2020-10-10 08:43:22 +10:00
Hailey
67abf35a28 Merge pull request #1 from SashLilac/master
Removed the BG limit, because someone is a madman
2020-10-10 08:02:00 +10:00
Hailey
629beb7240 Added fitting menu sfx 2020-10-10 07:50:05 +10:00
60 changed files with 1955 additions and 420 deletions

View File

@@ -1,3 +1,5 @@
![Cambridge Banner](https://cdn.discordapp.com/attachments/764432435802013709/767724895076614154/cambridge_logo_lt.png)
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.
![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png)
Installation instructions
-------------------------

View File

@@ -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

Binary file not shown.

BIN
libs/discord-rpc.dylib Normal file

Binary file not shown.

BIN
libs/discord-rpc.so Normal file

Binary file not shown.

268
libs/discordRPC.lua Normal file
View 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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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)

View File

@@ -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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
res/img/bonew.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
res/se/cursor.wav Normal file

Binary file not shown.

BIN
res/se/cursor_lr.wav Normal file

Binary file not shown.

BIN
res/se/main_decide.wav Normal file

Binary file not shown.

BIN
res/se/mode_decide.wav Normal file

Binary file not shown.

View File

@@ -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"

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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)

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -5,6 +5,18 @@ 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
@@ -19,10 +31,10 @@ SRS.spawn_positions = {
}
SRS.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 },
@@ -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