Compare commits

..

44 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
ffd808e6a0 Added white and black as their own separate colors...
... instead of borrowing from the lock flash / garbage colors
2021-09-21 23:30:51 -04:00
Ishaan Bhardwaj
dd96db170e newline 2021-09-21 18:01:36 -04:00
Ishaan Bhardwaj
7fa547c307 Two quick changes (read comments)
Added mouse wheel support to the mode select menu
BGM now interatcs with pausing correctly
2021-09-20 23:33:27 -04:00
b2d0838f90 Pull bigint.lua from bigint.lua repo 2021-09-20 16:09:02 -04:00
Ishaan Bhardwaj
42375cb2b8 Changed mode select DAS to 15/4 (old 24/6) 2021-09-16 18:05:38 -04:00
Ishaan Bhardwaj
fe162ed215 Mode select changes (read below)
Added DAS to the up/down actions (24F start-up, 6F period)
Added wheel scroll to the up/down/left/right actions
Added a warning in case somehow the player has no modes or rulesets
Mode select will load new modules every time you access it
However, this does not reload changes to existing modules
2021-09-16 14:54:49 -04:00
Brandon McGriff
dda116f00f Merge branch 'master' of https://github.com/SashLilac/cambridge 2021-09-15 17:54:26 -07:00
Brandon McGriff
2d3aeeb47d Fix loading of discordRPC when source path contains non-ASCII characters 2021-09-15 17:54:22 -07:00
Ishaan Bhardwaj
784c768c57 Update SOURCES.md 2021-09-14 22:15:08 -04:00
Ishaan Bhardwaj
c18e7ed244 Fixed hold opacity when level < 1000 2021-09-12 19:28:00 -04:00
Ishaan Bhardwaj
9df6bb9989 Small clean-up in PM2 2021-09-12 19:26:47 -04:00
Brandon McGriff
f5873c97bc Remove debugging prints in save.lua 2021-09-12 14:36:21 -07:00
Brandon McGriff
fabdad056e Fix save data handling when save data directory contains non-ASCII characters 2021-09-12 14:32:21 -07:00
Joe Zeng
0e82a8758c Merge pull request #34 from Kirby703/patch-3
made mode extensible
2021-09-11 23:47:11 -04:00
Kirby703
e78df19112 made mode extensible 2021-09-11 22:39:20 -04:00
Ishaan Bhardwaj
49775b9578 Fixed onEnterOrHold running twice on IHS 2021-09-11 18:19:03 -04:00
Ishaan Bhardwaj
6a3c6ecac0 Changed fullscreen bind to F11 2021-09-09 19:02:38 -04:00
Ishaan Bhardwaj
90cf2ebef5 New onEnterOrHold function (fixes #29) 2021-09-05 23:08:54 -04:00
Ishaan Bhardwaj
799a905a9c Remove redundant if 2021-09-05 22:50:31 -04:00
Ishaan Bhardwaj
985f73c39d Revert "Yet more SOCD handling"
This reverts commit b5db5bbdc3.
2021-09-05 22:44:21 -04:00
Ishaan Bhardwaj
b5db5bbdc3 Yet more SOCD handling 2021-09-04 22:45:20 -04:00
Ishaan Bhardwaj
438acde2e2 Better (default) SOCD handling 2021-09-04 22:33:53 -04:00
Ishaan Bhardwaj
0e1f40ad30 Amend the copying functions 2021-08-27 17:18:06 -04:00
Ishaan Bhardwaj
6cf6568a57 Revert "Fixed spawn positions on larger than 10w boards"
This reverts commit dafc113038.

This didn't actually fix the problem, so it's been reverted.
2021-08-20 19:09:50 -04:00
Ishaan Bhardwaj
dafc113038 Fixed spawn positions on larger than 10w boards 2021-08-20 18:53:07 -04:00
Ishaan Bhardwaj
923f3d3696 Added drawIfPaused to gamemode.lua 2021-08-19 14:16:34 -04:00
Ishaan Bhardwaj
db4132bf31 License update, added more credits 2021-08-19 14:15:57 -04:00
Ishaan Bhardwaj
c58018dd51 Two changes to main.lua (read comments)
Disallowed trying to load a directory
Required funcs.lua at the beginning of the program so mod makers don't have to anywhere else
2021-08-15 23:50:00 -04:00
Ishaan Bhardwaj
c7d0034f9b Two changes to gamemode.lua (read comments)
Shape is now passed as an argument to ruleset:getDefaultOrientation()
Fixed the comment for GameMode:transformScreen()
2021-08-11 19:44:57 -04:00
Ishaan Bhardwaj
ed5ea72e66 Cleaning up ruleset.lua 2021-08-11 19:30:46 -04:00
Ishaan Bhardwaj
dc3ad825dc Fix to #27 + some other gamemode functionality 2021-08-09 00:29:22 -04:00
Ishaan Bhardwaj
40cba83003 Fixed a bug with the volume sliders...
...where the SFX that played upon changing the slider's value...
...reflected the old value instead of the new one.
2021-08-04 16:46:41 -04:00
a1b3f73787 Merge pull request #25 from Kirby703/patch-2
fix a3 cools
2021-07-29 23:41:27 -04:00
Kirby703
4243d6b2ba fix a3 cools 2021-07-28 18:38:22 -04:00
33b3ad2889 Merge pull request #24 from Kirby703/patch-1
0xx-3xx line clear delay fix
2021-07-28 10:42:09 -04:00
Kirby703
adab1df480 0xx-3xx line clear delay fix 2021-07-28 05:22:26 -04:00
Joe Z
711fa830a3 Added a few minutes to the torikans. 2021-07-18 22:20:51 -04:00
Joe Z
c434a3406b Changed the 2-second rule to give the cool at exactly 2 seconds. 2021-07-18 21:23:50 -04:00
Joe Zeng
769b5043e3 Added a 25,000 grade point requirement to the GM roll.
You need to go a _little_ further than the point grade of 30 to qualify for GM.
2021-07-18 00:25:05 -04:00
Ishaan Bhardwaj
713c62d807 Fixed Death giving GM below 999 2021-07-18 00:13:57 -04:00
Ishaan Bhardwaj
c3f6e34518 New build scripts for targets other than Windows 2021-07-17 23:08:01 -04:00
Ishaan Bhardwaj
4d0f6ab9fc Easier-to-see bone blocks 2021-07-17 16:25:16 -04:00
Ishaan Bhardwaj
594aa2620f Added another game to the notable games section 2021-07-16 16:50:27 -04:00
Ishaan Bhardwaj
199b535f70 Added a game to the README 2021-07-16 16:24:01 -04:00
28 changed files with 373 additions and 148 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
*.sav
*.love
*.zip
dist/*.zip
dist/**/cambridge.exe
dist/**/libs

View File

@@ -1,4 +1,4 @@
Copyright (c) 2018-2019 Joe Zeng
Copyright (c) 2018-2021 Joe Zeng, Ishaan Bhardwaj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -98,12 +98,14 @@ Other Notable Games
- [ZTrix](https://discord.gg/MGhqCBDGNH) by Electra
- [Shiromino](https://github.com/shiromino/shiromino) by Felicity/nightmareci/kdex
- [Cursed Blocks](https://github.com/Manabender/Cursed-Blocks) by Manabender
- Picoris 1/2 by MarkGamed
- [Picoris 2](https://www.lexaloffle.com/bbs/?tid=41733) by MarkGamed
- [Tetra Online](https://github.com/Juan-Cartes/Tetra-Offline) by Mine
- [Techmino](https://discord.gg/6Yuww44tq8) by MrZ
- [Example Block Game](https://github.com/oshisaure/example-block-game) by Oshisaure
- [TETR.IO](https://tetr.io) by osk
- [Master of Blocks](https://discord.gg/72FZ49mjWh) by Phoenix Flare
- [Spirit Drop](https://rayblastgames.com/spiritdrop.php) by RayRay26
- [Puzzle Trial](https://kagamine-rin.itch.io/puzzle-trial) by Rin
- [stackfuse](https://github.com/sinefuse/stackfuse) by sinefuse
![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png)

View File

@@ -170,3 +170,51 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
discord-rpc (https://github.com/discord/discord-rpc)
--------------------
Copyright 2017 Discord, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
lua-discordRPC (https://github.com/pfirsich/lua-discordRPC)
--------------------
MIT License
Copyright (c) 2018 Joel Schumacher
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -7,5 +7,11 @@
@del dist\win32\SOURCES.md
@del dist\win32\LICENSE.md
@rmdir /Q /S dist\win32\libs
@del dist\other\cambridge.love
@del dist\other\SOURCES.md
@del dist\other\LICENSE.md
@rmdir /Q /S dist\other\libs
@rmdir /Q /S dist\other
@del dist\cambridge-windows.zip
@del dist\cambridge-win32.zip
@del dist\cambridge-other.zip

View File

@@ -1,10 +1,20 @@
function copy(t)
-- returns deep copy of t (as opposed to the shallow copy you get from var = t)
-- returns top-layer shallow copy of t
if type(t) ~= "table" then return t end
local meta = getmetatable(t)
local target = {}
for k, v in pairs(t) do target[k] = v end
setmetatable(target, meta)
for k, v in next, t do target[k] = v end
setmetatable(target, getmetatable(t))
return target
end
function deepcopy(t)
-- returns infinite-layer deep copy of t
if type(t) ~= "table" then return t end
local target = {}
for k, v in next, t do
target[deepcopy(k)] = deepcopy(v)
end
setmetatable(target, deepcopy(getmetatable(t)))
return target
end

View File

@@ -2,11 +2,12 @@
-- If this variable is true, then strict type checking is performed for all
-- operations. This may result in slower code, but it will allow you to catch
-- errors and bugs earlier.
local strict = false
local strict = true
--------------------------------------------------------------------------------
local bigint = {}
setmetatable(bigint, {__call = function(_, arg) return bigint.new(arg) end})
local mt = {
__add = function(lhs, rhs)
@@ -76,7 +77,7 @@ function bigint.new(num)
end
end
return self
return bigint.strip(self)
end
-- Check the type of a big
@@ -96,6 +97,14 @@ function bigint.check(big, force)
return true
end
-- Strip leading zeroes from a big, but don't remove the last zero
function bigint.strip(big)
while (#big.digits > 1) and (big.digits[1] == 0) do
table.remove(big.digits, 1)
end
return big
end
-- Return a new big with the same digits but with a positive sign (absolute
-- value)
function bigint.abs(big)
@@ -329,12 +338,7 @@ function bigint.subtract_raw(big1, big2)
----------------------------------------------------------------------------
-- Strip leading zeroes if any, but not if 0 is the only digit
while (#result.digits > 1) and (result.digits[1] == 0) do
table.remove(result.digits, 1)
end
return result
return bigint.strip(result)
end
-- FRONTEND: Addition and subtraction operations, accounting for signs
@@ -364,6 +368,7 @@ function bigint.add(big1, big2)
return result
end
function bigint.subtract(big1, big2)
-- Type checking is done by bigint.compare in bigint.add
-- Subtracting is like adding a negative
@@ -460,7 +465,7 @@ end
function bigint.exponentiate(big, power)
-- Type checking for big done by bigint.multiply
assert(bigint.compare(power, bigint.new(0), ">="),
" negative powers are not supported")
"negative powers are not supported")
local exp = power:clone()
if (bigint.compare(exp, bigint.new(0), "==")) then
@@ -530,12 +535,7 @@ function bigint.divide_raw(big1, big2)
end
end
-- Remove leading zeros from result
while (result.digits[1] == 0) do
table.remove(result.digits, 1)
end
return result, dividend
return bigint.strip(result), dividend
end
end

View File

@@ -24,6 +24,59 @@ if osname == "Linux" then
elseif osname == "OS X" then
discordRPClib = ffi.load(source.."/libs/discord-rpc.dylib")
elseif osname == "Windows" then
-- I would strongly advise never touching this. It was not trivial to get correct. -nightmareci
ffi.cdef[[
typedef uint32_t DWORD;
typedef char CHAR;
typedef CHAR *LPSTR;
typedef const CHAR *LPCSTR;
typedef wchar_t WCHAR;
typedef WCHAR *LPWSTR;
typedef LPWSTR PWSTR;
typedef const WCHAR *LPCWSTR;
static const DWORD CP_UTF8 = 65001;
int32_t MultiByteToWideChar(
DWORD CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int32_t cbMultiByte,
LPWSTR lpWideCharStr,
int32_t cchWideChar
);
int32_t WideCharToMultiByte(
DWORD CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int32_t cchWideChar,
LPSTR lpMultiByteStr,
int32_t cbMultiByte,
void* lpDefaultChar,
void* lpUsedDefaultChar
);
DWORD GetShortPathNameW(
LPCWSTR lpszLongPath,
LPWSTR lpszShortPath,
DWORD cchBuffer
);
]]
local originalWideSize = ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, nil, 0)
local originalWide = ffi.new('WCHAR[?]', originalWideSize)
ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, originalWide, originalWideSize)
local sourceSize = ffi.C.GetShortPathNameW(originalWide, nil, 0)
local sourceWide = ffi.new('WCHAR[?]', sourceSize)
ffi.C.GetShortPathNameW(originalWide, sourceWide, sourceSize)
local sourceChar = ffi.new('char[?]', sourceSize)
ffi.C.WideCharToMultiByte(ffi.C.CP_UTF8, 0, sourceWide, sourceSize, sourceChar, sourceSize, nil, nil)
source = ffi.string(sourceChar)
discordRPClib = ffi.load(source.."/libs/discord-rpc.dll")
else
-- Else it crashes later on

View File

@@ -7,6 +7,7 @@ bgm = {
local current_bgm = nil
local bgm_locked = false
local unfocused = false
function switchBGM(sound, subsound)
if current_bgm ~= nil then
@@ -56,7 +57,7 @@ end
function resetBGMFadeout(time)
current_bgm:setVolume(config.bgm_volume)
fading_bgm = false
current_bgm:play()
resumeBGM()
end
function processBGMFadeout(dt)
@@ -70,13 +71,20 @@ function processBGMFadeout(dt)
end
end
function pauseBGM()
function pauseBGM(f)
if f then
unfocused = true
end
if current_bgm ~= nil then
current_bgm:pause()
end
end
function resumeBGM()
function resumeBGM(f)
if f and scene.paused and unfocused then
unfocused = false
return
end
if current_bgm ~= nil then
current_bgm:play()
end

View File

@@ -25,6 +25,15 @@ backgrounds = {
game_config = love.graphics.newImage("res/backgrounds/options-game.png"),
}
-- in order, the colors are:
-- red, orange, yellow, green, cyan, blue
-- magenta (or purple), white, black
-- the next three don't have colors tied to them
-- F is used for lock flash
-- A is a garbage block
-- X is an invisible "block"
-- don't use these for piece colors when making a ruleset
-- all the others are fine to use
blocks = {
["2tie"] = {
R = love.graphics.newImage("res/img/s1.png"),
@@ -34,6 +43,8 @@ blocks = {
C = love.graphics.newImage("res/img/s2.png"),
B = love.graphics.newImage("res/img/s4.png"),
M = love.graphics.newImage("res/img/s5.png"),
W = love.graphics.newImage("res/img/s9.png"),
D = love.graphics.newImage("res/img/s8.png"),
F = love.graphics.newImage("res/img/s9.png"),
A = love.graphics.newImage("res/img/s8.png"),
X = love.graphics.newImage("res/img/s9.png"),
@@ -46,6 +57,8 @@ blocks = {
C = love.graphics.newImage("res/img/bone.png"),
B = love.graphics.newImage("res/img/bone.png"),
M = love.graphics.newImage("res/img/bone.png"),
W = love.graphics.newImage("res/img/bone.png"),
D = love.graphics.newImage("res/img/bone.png"),
F = love.graphics.newImage("res/img/bone.png"),
A = love.graphics.newImage("res/img/bone.png"),
X = love.graphics.newImage("res/img/bone.png"),
@@ -58,13 +71,16 @@ blocks = {
C = love.graphics.newImage("res/img/gem2.png"),
B = love.graphics.newImage("res/img/gem4.png"),
M = love.graphics.newImage("res/img/gem5.png"),
W = love.graphics.newImage("res/img/gem9.png"),
D = love.graphics.newImage("res/img/gem9.png"),
F = love.graphics.newImage("res/img/gem9.png"),
A = love.graphics.newImage("res/img/gem9.png"),
X = love.graphics.newImage("res/img/gem9.png"),
},
["square"] = {
F = love.graphics.newImage("res/img/squares.png"),
W = love.graphics.newImage("res/img/squares.png"),
Y = love.graphics.newImage("res/img/squareg.png"),
F = love.graphics.newImage("res/img/squares.png"),
X = love.graphics.newImage("res/img/squares.png"),
}
}
@@ -87,7 +103,7 @@ ColourSchemes = {
Z = "R",
O = "Y",
T = "M",
},
}
}
for name, blockset in pairs(blocks) do

View File

@@ -1,23 +1,16 @@
local binser = require 'libs.binser'
function loadSave()
local info = love.filesystem.getInfo(
love.filesystem.getSaveDirectory(), "directory"
)
if not info then
love.filesystem.remove(love.filesystem.getSaveDirectory())
love.filesystem.createDirectory(love.filesystem.getSaveDirectory())
end
config = loadFromFile(
love.filesystem.getSaveDirectory() .. '/config.sav'
)
highscores = loadFromFile(
love.filesystem.getSaveDirectory() .. '/highscores.sav'
)
config = loadFromFile('config.sav')
highscores = loadFromFile('highscores.sav')
end
function loadFromFile(filename)
local save_data, len = binser.readFile(filename)
local file_data = love.filesystem.read(filename)
if file_data == nil then
return {} -- new object
end
local save_data = binser.deserialize(file_data)
if save_data == nil then
return {} -- new object
end
@@ -51,13 +44,13 @@ function initConfig()
end
function saveConfig()
binser.writeFile(
love.filesystem.getSaveDirectory() .. '/config.sav', config
love.filesystem.write(
'config.sav', binser.serialize(config)
)
end
function saveHighscores()
binser.writeFile(
love.filesystem.getSaveDirectory() .. '/highscores.sav', highscores
love.filesystem.write(
'highscores.sav', binser.serialize(highscores)
)
end

View File

@@ -10,6 +10,7 @@ function love.load()
require "load.bigint"
require "load.version"
loadSave()
require "funcs"
require "scene"
--config["side_next"] = false
@@ -36,14 +37,14 @@ function initModules()
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
if(mode_list[i] ~= "gamemode.lua" and string.sub(mode_list[i], -4) == ".lua") 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
if(rule_list[i] ~= "ruleset.lua" and string.sub(rule_list[i], -4) == ".lua") then
rulesets[#rulesets+1] = require ("tetris.rulesets."..string.sub(rule_list[i],1,-5))
end
end
@@ -82,7 +83,7 @@ end
function love.keypressed(key, scancode)
-- global hotkeys
if scancode == "f4" then
if scancode == "f11" then
config["fullscreen"] = not config["fullscreen"]
saveConfig()
love.window.setFullscreen(config["fullscreen"])
@@ -258,11 +259,15 @@ function love.joystickhat(joystick, hat, direction)
end
end
function love.wheelmoved(x, y)
scene:onInputPress({input=nil, type="wheel", x=x, y=y})
end
function love.focus(f)
if f and (scene.title ~= "Game" or not scene.paused) then
resumeBGM()
if f then
resumeBGM(true)
else
pauseBGM()
pauseBGM(true)
end
end

View File

@@ -2,8 +2,10 @@
mkdir dist
mkdir dist/windows
mkdir dist/win32
cp cambridge.love dist/
mkdir dist/other
cat dist/windows/love.exe cambridge.love > dist/windows/cambridge.exe
zip dist/cambridge-windows.zip dist/windows/* SOURCES.md LICENSE.md
cat dist/win32/love.exe cambridge.love > dist/win32/cambridge.exe
zip dist/cambridge-win32.zip dist/win32/* SOURCES.md LICENSE.md
cp cambridge.love dist/other/
zip dist/cambridge-other.zip cambridge.love libs/discord-rpc.* SOURCES.md LICENSE.md

View File

@@ -5,17 +5,23 @@ mkdir dist\windows
mkdir dist\windows\libs
mkdir dist\win32
mkdir dist\win32\libs
mkdir dist\other
mkdir dist\other\libs
copy /b dist\windows\love.exe+cambridge.love dist\windows\cambridge.exe
copy /b dist\win32\love.exe+cambridge.love dist\win32\cambridge.exe
copy /b cambridge.love dist\other\cambridge.love
copy libs\discord-rpc.dll dist\windows\libs
copy libs\discord-rpc.dll dist\win32\libs
copy libs\discord-rpc.* dist\other\libs
copy SOURCES.md dist\windows
copy LICENSE.md dist\windows
copy SOURCES.md dist\win32
copy LICENSE.md dist\win32
copy SOURCES.md dist\other
copy LICENSE.md dist\other
cd dist\windows
tar -a -c -f ..\cambridge-windows.zip cambridge.exe *.dll libs *.md
@@ -24,3 +30,7 @@ cd ..\..
cd dist\win32
tar -a -c -f ..\cambridge-win32.zip cambridge.exe *.dll libs *.md
cd ..\..
cd dist\other
tar -a -c -f ..\cambridge-other.zip cambridge.love libs *.md
cd ..\..

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 B

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -5,7 +5,7 @@ CreditsScene.title = "Credits"
function CreditsScene:new()
self.frames = 0
-- higher = slower
self.scroll_speed = 1.9
self.scroll_speed = 1.85
switchBGM("credit_roll", "gm3")
end
@@ -34,14 +34,14 @@ function CreditsScene:render()
love.graphics.setFont(font_3x5_4)
love.graphics.print("Cambridge Credits", 320, 500 - offset)
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(2010 - offset, 240))
love.graphics.print("THANK YOU\nFOR PLAYING!", 320, math.max(2030 - offset, 240))
love.graphics.setFont(font_3x5_3)
love.graphics.print("Game Developers", 320, 550 - offset)
love.graphics.print("Project Heads", 320, 640 - offset)
love.graphics.print("Notable Game Developers", 320, 730 - offset)
love.graphics.print("Special Thanks", 320, 980 - offset)
love.graphics.print("- Milla", 320, math.max(2090 - offset, 320))
love.graphics.print("Special Thanks", 320, 1000 - offset)
love.graphics.print("- Milla", 320, math.max(2110 - offset, 320))
love.graphics.setFont(font_3x5_2)
love.graphics.print("Oshisaure\nJoe Zeng", 320, 590 - offset)
@@ -51,7 +51,7 @@ function CreditsScene:render()
"Electra - ZTrix\nFelicity/nightmareci/kdex - Shiromino\n" ..
"Mine - Tetra Online\nMrZ - Techmino\nosk - TETR.IO\n" ..
"Phoenix Flare - Master of Blocks\nRayRay26 - Spirit Drop\n" ..
"sinefuse - stackfuse",
"Rin - Puzzle Trial\nsinefuse - stackfuse",
320, 770 - offset
)
love.graphics.print(
@@ -69,7 +69,7 @@ function CreditsScene:render()
"Tetra Legends Discord\nTetra Online Discord\nMultimino Discord\n" ..
"Hard Drop Discord\nRusty's Systemspace\nCambridge Discord\n" ..
"And to you, the player!",
320, 1020 - offset
320, 1040 - offset
)
end

View File

@@ -100,9 +100,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]-1, #option[4])
else
playSE("cursor")
sld = self[self.options[self.highlight][4]]
local sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() - 5) / (sld.max - sld.min)))
sld:update()
playSE("cursor")
end
elseif e.input == "right" or e.scancode == "right" then
if not self.options[self.highlight][3] then
@@ -110,9 +111,10 @@ function ConfigScene:onInputPress(e)
local option = ConfigScene.options[self.highlight]
config.gamesettings[option[1]] = Mod1(config.gamesettings[option[1]]+1, #option[4])
else
playSE("cursor")
sld = self[self.options[self.highlight][4]]
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))--math.max(0, (math.floor(sld:getValue())+2)/(sld.max-sld.min))
sld.value = math.max(sld.min, math.min(sld.max, (sld:getValue() + 5) / (sld.max - sld.min)))
sld:update()
playSE("cursor")
end
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
loadSave()

View File

@@ -6,6 +6,22 @@ current_mode = 1
current_ruleset = 1
function ModeSelectScene:new()
-- reload custom modules
initModules()
if table.getn(game_modes) == 0 or table.getn(rulesets) == 0 then
self.display_warning = true
current_mode = 1
current_ruleset = 1
else
self.display_warning = false
if current_mode > table.getn(game_modes) then
current_mode = 1
end
if current_ruleset > table.getn(rulesets) then
current_ruleset = 1
end
end
self.menu_state = {
mode = current_mode,
ruleset = current_ruleset,
@@ -19,6 +35,7 @@ function ModeSelectScene:new()
rotate_180 = false,
hold = false,
}
self.das = 0
DiscordRPC:update({
details = "In menus",
state = "Choosing a mode",
@@ -27,6 +44,17 @@ end
function ModeSelectScene:update()
switchBGM(nil) -- experimental
if self.das_up or self.das_down then
self.das = self.das + 1
else
self.das = 0
end
if self.das >= 15 then
self:changeOption(self.das_up and -1 or 1)
self.das = self.das - 4
end
end
function ModeSelectScene:render()
@@ -36,6 +64,23 @@ function ModeSelectScene:render()
0.5, 0.5
)
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
if self.display_warning then
love.graphics.setFont(font_3x5_3)
love.graphics.printf(
"You have no modes or rulesets.",
80, 200, 480, "center"
)
love.graphics.setFont(font_3x5_2)
love.graphics.printf(
"Come back to this menu after getting more modes or rulesets. " ..
"Press any button to return to the main menu.",
80, 250, 480, "center"
)
return
end
if self.menu_state.select == "mode" then
love.graphics.setColor(1, 1, 1, 0.5)
elseif self.menu_state.select == "ruleset" then
@@ -52,8 +97,6 @@ function ModeSelectScene:render()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(misc_graphics["select_mode"], 20, 40)
love.graphics.setFont(font_3x5_2)
for idx, mode in pairs(game_modes) do
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
@@ -68,23 +111,37 @@ function ModeSelectScene:render()
end
function ModeSelectScene:onInputPress(e)
if e.input == "menu_decide" or e.scancode == "return" then
if self.display_warning and e.input then
scene = TitleScene()
elseif e.type == "wheel" then
if e.x % 2 == 1 then
self:switchSelect()
end
if e.y ~= 0 then
self:changeOption(-e.y)
end
elseif e.input == "menu_decide" or e.scancode == "return" then
current_mode = self.menu_state.mode
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], self.secret_inputs)
scene = GameScene(
game_modes[self.menu_state.mode],
rulesets[self.menu_state.ruleset],
self.secret_inputs
)
elseif e.input == "up" or e.scancode == "up" then
self:changeOption(-1)
playSE("cursor")
self.das_up = true
self.das_down = nil
elseif e.input == "down" or e.scancode == "down" then
self:changeOption(1)
playSE("cursor")
self.das_down = true
self.das_up = nil
elseif e.input == "left" or e.input == "right" or e.scancode == "left" or e.scancode == "right" then
self:switchSelect()
playSE("cursor_lr")
elseif e.input == "menu_back" or e.scancode == "delete" or e.scancode == "backspace" then
scene = TitleScene()
elseif e.input then
@@ -95,6 +152,10 @@ end
function ModeSelectScene:onInputRelease(e)
if e.input == "hold" or (e.input and string.sub(e.input, 1, 7) == "rotate_") then
self.secret_inputs[e.input] = false
elseif e.input == "up" or e.scancode == "up" then
self.das_up = nil
elseif e.input == "down" or e.scancode == "down" then
self.das_down = nil
end
end
@@ -104,24 +165,26 @@ function ModeSelectScene:changeOption(rel)
elseif self.menu_state.select == "ruleset" then
self:changeRuleset(rel)
end
playSE("cursor")
end
function ModeSelectScene:switchSelect(rel)
function ModeSelectScene:switchSelect()
if self.menu_state.select == "mode" then
self.menu_state.select = "ruleset"
elseif self.menu_state.select == "ruleset" then
self.menu_state.select = "mode"
end
playSE("cursor_lr")
end
function ModeSelectScene:changeMode(rel)
local len = table.getn(game_modes)
self.menu_state.mode = (self.menu_state.mode + len + rel - 1) % len + 1
self.menu_state.mode = Mod1(self.menu_state.mode + rel, len)
end
function ModeSelectScene:changeRuleset(rel)
local len = table.getn(rulesets)
self.menu_state.ruleset = (self.menu_state.ruleset + len + rel - 1) % len + 1
self.menu_state.ruleset = Mod1(self.menu_state.ruleset + rel, len)
end
return ModeSelectScene

View File

@@ -349,7 +349,7 @@ function Grid:markSquares()
elseif i == 2 then
for j = 0, 3 do
for k = 0, 3 do
self.grid[y+j][x+k].colour = "F"
self.grid[y+j][x+k].colour = "W"
self.grid[y+j][x+k].skin = "square"
end

View File

@@ -126,9 +126,6 @@ function GameMode:update(inputs, ruleset)
if inputs["left"] or inputs["right"] then
inputs["up"] = false
inputs["down"] = false
elseif inputs["up"] or inputs["down"] then
inputs["left"] = false
inputs["right"] = false
end
end
@@ -490,20 +487,15 @@ end
function GameMode:initializeOrHold(inputs, ruleset)
if (
(self.frames == 0 or (ruleset.are and self:getARE() ~= 0)) and self.ihs or false
(self.frames == 0 or (ruleset.are and self:getARE() ~= 0))
and self.ihs or false
) and self.enable_hold and inputs["hold"] == true then
self:hold(inputs, ruleset, true)
else
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
end
self:onPieceEnter()
if not self.grid:canPlacePiece(self.piece) then
self.game_over = true
end
ruleset:dropPiece(
inputs, self.piece, self.grid, self:getGravity(),
self:getDropSpeed(), self.drop_locked, self.hard_drop_locked
)
self:onEnterOrHold(inputs, ruleset)
end
function GameMode:hold(inputs, ruleset, ihs)
@@ -516,7 +508,7 @@ function GameMode:hold(inputs, ruleset, ihs)
self.hold_queue = {
skin = self.piece.skin,
shape = self.piece.shape,
orientation = ruleset:getDefaultOrientation(),
orientation = ruleset:getDefaultOrientation(self.piece.shape),
}
end
if data == nil then
@@ -525,21 +517,35 @@ function GameMode:hold(inputs, ruleset, ihs)
self:initializeNextPiece(inputs, ruleset, data, false)
end
self.held = true
if ihs then playSE("ihs")
else playSE("hold") end
self:onHold()
if not self.grid:canPlacePiece(self.piece) then
self.game_over = true
if ihs then
playSE("ihs")
else
playSE("hold")
self:onEnterOrHold(inputs, ruleset)
end
end
function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next_piece)
if not self.buffer_soft_drop and self.lock_drop or (
function GameMode:onEnterOrHold(inputs, ruleset)
if not self.grid:canPlacePiece(self.piece) then
self.game_over = true
return
end
ruleset:dropPiece(
inputs, self.piece, self.grid, self:getGravity(),
self:getDropSpeed(), self.drop_locked, self.hard_drop_locked
)
end
function GameMode:initializeNextPiece(
inputs, ruleset, piece_data, generate_next_piece
)
if not inputs.hold and not self.buffer_soft_drop and self.lock_drop or (
not ruleset.are or self:getARE() == 0
) then
self.drop_locked = true
end
if not self.buffer_hard_drop and self.lock_hard_drop or (
if not inputs.hold and not self.buffer_hard_drop and self.lock_hard_drop or (
not ruleset.are or self:getARE() == 0
) then
self.hard_drop_locked = true
@@ -683,7 +689,9 @@ function GameMode:drawPiece()
end
function GameMode:drawGhostPiece(ruleset)
if self.piece == nil then return end
if self.piece == nil or not self.grid:canPlacePiece(self.piece) then
return
end
local ghost_piece = self.piece:withOffset({x=0, y=0})
ghost_piece.ghost = true
ghost_piece:dropToBottom(self.grid)
@@ -853,15 +861,7 @@ function GameMode:drawFrame()
-- game frame
if self.grid.width == 10 and self.grid.height == 24 then
love.graphics.draw(misc_graphics["frame"], 48, 64)
end
love.graphics.setColor(0, 0, 0, 200)
love.graphics.rectangle(
"fill", 64, 80,
16 * self.grid.width, 16 * (self.grid.height - 4)
)
if self.grid.width ~= 10 or self.grid.height ~= 24 then
else
love.graphics.setColor(174/255, 83/255, 76/255, 1)
love.graphics.setLineWidth(8)
love.graphics.line(
@@ -881,6 +881,11 @@ function GameMode:drawFrame()
60,76
)
love.graphics.setLineWidth(1)
love.graphics.setColor(0, 0, 0, 200)
love.graphics.rectangle(
"fill", 64, 80,
16 * self.grid.width, 16 * (self.grid.height - 4)
)
end
end
@@ -897,7 +902,20 @@ end
function GameMode:drawCustom() end
function GameMode:drawIfPaused()
love.graphics.setFont(font_3x5_3)
love.graphics.printf("GAME PAUSED!", 64, 160, 160, "center")
end
-- transforms specified in here will transform the whole screen
-- if you want a transform for a particular component, push the
-- default transform by using love.graphics.push(), do your
-- transform, and then love.graphics.pop() at the end of that
-- component's draw call!
function GameMode:transformScreen() end
function GameMode:draw(paused)
self:transformScreen()
self:drawBackground()
self:drawFrame()
self:drawGrid()
@@ -919,9 +937,8 @@ function GameMode:draw(paused)
)
end
love.graphics.setFont(font_3x5_3)
if paused then
love.graphics.printf("GAME PAUSED!", 64, 160, 160, "center")
self:drawIfPaused()
end
if self.completed then

View File

@@ -290,10 +290,10 @@ function Marathon2020Game:sectionPassed(old_level, new_level)
end
function Marathon2020Game:checkTorikan(section)
if section == 5 and self.frames < frameTime(6,00,00) then self.torikan_passed[500] = true end
if section == 9 and self.frames < frameTime(8,30,00) then self.torikan_passed[900] = true end
if section == 10 and self.frames < frameTime(8,45,00) then self.torikan_passed[1000] = true end
if section == 15 and self.frames < frameTime(11,30,00) then self.torikan_passed[1500] = true end
if section == 5 and self.frames < frameTime(8,00,00) then self.torikan_passed[500] = true end
if section == 9 and self.frames < frameTime(10,30,00) then self.torikan_passed[900] = true end
if section == 10 and self.frames < frameTime(10,45,00) then self.torikan_passed[1000] = true end
if section == 15 and self.frames < frameTime(12,30,00) then self.torikan_passed[1500] = true end
if section == 19 and self.frames < frameTime(13,15,00) then self.torikan_passed[1900] = true end
end
@@ -354,7 +354,7 @@ function Marathon2020Game:updateSectionTimes(old_level, new_level)
if (
self.section_status[section - 1] == "cool" and
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 and
self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120 and
self.secondary_section_times[section] < cool_cutoffs[self.delay_level]
) then
sectionCool(section)
@@ -407,11 +407,12 @@ GM-roll requirements
You qualify for the GM roll if you:
- Reach level 2020
- with a grade of 50
- and at least 25,000 grade points
- in less than 13:30.00 total.
]]--
return self.level >= 2020 and self:getTotalGrade() == 50 and self.frames <= frameTime(13,30)
return self.level >= 2020 and self:getTotalGrade() == 50 and self.grade_points >= 25000 and self.frames <= frameTime(13,30)
end
function Marathon2020Game:drawGrid()

View File

@@ -218,16 +218,11 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
section_70_time = self.frames - self.section_start_time
table.insert(self.secondary_section_times, section_70_time)
if section <= 9 and self.section_status[section - 1] == "cool" and
self.secondary_section_times[section] < self.secondary_section_times[section - 1] + 120 then
if section <= 9 and self.secondary_section_times[section] < cool_cutoffs[section] and
(section == 1 or self.secondary_section_times[section] <= self.secondary_section_times[section - 1] + 120) then
self.section_cool = true
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
elseif self.section_status[section - 1] == "cool" then self.section_cool = false
elseif section <= 9 and self.secondary_section_times[section] < cool_cutoffs[section] then
self.section_cool = true
self.coolregret_message = "COOL!!"
self.coolregret_timer = 300
self.coolregret_timer = 300
end
end
end

View File

@@ -41,7 +41,7 @@ function PhantomManiaGame:getARE()
end
function PhantomManiaGame:getLineARE()
if self.level < 100 then return 18
if self.level < 100 then return 14
elseif self.level < 400 then return 8
elseif self.level < 500 then return 7
else return 6 end

View File

@@ -179,7 +179,7 @@ function PhantomMania2Game:onPieceLock(piece, cleared_row_count)
end
function PhantomMania2Game:onHold()
self.super.onHold()
self.super:onHold()
self.hold_age = 0
end
@@ -287,7 +287,8 @@ function PhantomMania2Game:setHoldOpacity()
if self.level > 1000 and self.level < 1300 then
love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.hold_age / 15))
else
self.super:setHoldOpacity(1, self.held and 0.6 or 1)
local colour = self.held and 0.6 or 1
love.graphics.setColor(colour, colour, colour, 1)
end
end

View File

@@ -97,14 +97,13 @@ end
function SurvivalA2Game:onLineClear(cleared_row_count)
if not self.clear then
local new_level = math.min(self.level + cleared_row_count, 999)
if self.level == 999 or self:hitTorikan(self.level, new_level) then
if new_level == 999 or self:hitTorikan(self.level, new_level) then
self.clear = true
if self.level < 999 then
if new_level < 999 then
self.game_over = true
end
else
self.level = new_level
end
self.level = new_level
end
end
@@ -158,7 +157,7 @@ function SurvivalA2Game:drawScoringInfo()
love.graphics.setFont(font_3x5_3)
love.graphics.printf(self.score, text_x, 220, 90, "left")
if self.roll_frames > 2968 then love.graphics.setColor(1, 0.5, 0, 1)
if self.roll_frames > 1800 then love.graphics.setColor(1, 0.5, 0, 1)
elseif self.level >= 999 and self.clear then love.graphics.setColor(0, 1, 0, 1) end
if self:getLetterGrade() ~= "" then love.graphics.printf(self:getLetterGrade(), text_x, 140, 90, "left") end
love.graphics.setColor(1, 1, 1, 1)

View File

@@ -47,18 +47,11 @@ function Ruleset:new(game_mode)
else
bones = config.gamesettings.piece_colour == 3 and "w" or ""
end
blocks.bone = {
R = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
O = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
Y = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
G = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
C = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
B = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
M = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
F = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
A = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
X = love.graphics.newImage("res/img/bone" .. bones .. ".png"),
}
for colour in pairs(blocks["2tie"]) do
blocks.bone[colour] = love.graphics.newImage(
"res/img/bone" .. bones .. ".png"
)
end
end
function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
@@ -162,18 +155,18 @@ function Ruleset:dropPiece(
hard_drop_enabled, additive_gravity, classic_lock
)
local y = piece.position.y
if inputs["down"] == true and drop_locked == false then
if additive_gravity then
piece:addGravity(gravity + drop_speed, grid, classic_lock)
else
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
end
elseif inputs["up"] == true and hard_drop_enabled == true then
if inputs["up"] == true and hard_drop_enabled == true then
if hard_drop_locked == true or piece:isDropBlocked(grid) then
piece:addGravity(gravity, grid, classic_lock)
else
piece:dropToBottom(grid)
end
elseif inputs["down"] == true and drop_locked == false then
if additive_gravity then
piece:addGravity(gravity + drop_speed, grid, classic_lock)
else
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
end
else
piece:addGravity(gravity, grid, classic_lock)
end
@@ -221,7 +214,7 @@ function Ruleset:initializePiece(
colours = self.colourscheme
end
local spawn_x = math.floor(spawn_positions[data.shape].x / 10 * grid.width)
local spawn_x = math.floor(spawn_positions[data.shape].x * grid.width / 10)
local spawn_dy
if (config.gamesettings.spawn_positions == 1) then
@@ -237,7 +230,7 @@ function Ruleset:initializePiece(
end
local piece = Piece(data.shape, data.orientation - 1, {
x = spawn_x or spawn_positions[data.shape].x,
x = spawn_x,
y = spawn_positions[data.shape].y - spawn_dy
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)