Compare commits

...

19 Commits

Author SHA1 Message Date
Ishaan Bhardwaj
351fb4cfe9 Added the functionality to draw only an outline of the stack 2020-11-18 12:17:04 -05:00
Ishaan Bhardwaj
103f04ceaa added a misc function 2020-11-17 21:52:20 -05:00
Ishaan Bhardwaj
88d2f0d8d1 Made ARE canceling more consistent. 2020-11-17 13:50:38 -05:00
Ishaan Bhardwaj
e100289c82 Made ARE canceling less slippery 2020-11-16 22:23:05 -05:00
Ishaan Bhardwaj
e38da49180 ARE canceling 2020-11-16 21:16:59 -05:00
Ishaan Bhardwaj
b03473d2fe IRS fix 2020-11-16 12:51:21 -05:00
Ishaan Bhardwaj
cf6e0be4e7 New IRS and IHS settings 2020-11-16 12:48:28 -05:00
Ishaan Bhardwaj
2bc9dc179c Updated README with another contributor 2020-11-14 20:00:24 -05:00
Ishaan Bhardwaj
d626926d5a Fixed 180 rotation directions 2020-11-14 19:20:25 -05:00
Ishaan Bhardwaj
721acefea0 Cleaned up TAP M-roll 2020-11-14 09:35:16 -05:00
Ishaan Bhardwaj
b9b71e90bb Finished Marathon 2020 grading! 2020-11-12 17:01:28 -05:00
Ishaan Bhardwaj
9f61b139fd TAP M-roll created 2020-11-12 16:52:40 -05:00
Ishaan Bhardwaj
3b0fdba27d Updated README.md to have the updated mod pack 2020-11-12 09:45:48 -05:00
Ishaan Bhardwaj
d8fad3dc37 Added some more developer functions, to aid in building modes. 2020-11-11 22:30:30 -05:00
Ishaan Bhardwaj
f9368fa806 Forgot to add a contributor, whoops... 2020-11-11 11:11:55 -05:00
Ishaan Bhardwaj
189feb1802 Fixed the last of the hard drop safelocks... 2020-11-10 23:13:25 -05:00
Ishaan Bhardwaj
dc09dabacb Fixed Phantom Mania safelock behaviors 2020-11-10 23:10:39 -05:00
Ishaan Bhardwaj
e13278c6a8 Fixed safelock behavior for hard drop modes 2020-11-10 22:34:48 -05:00
Ishaan Bhardwaj
f7f11b0e22 Updated README.md with new Windows instructions 2020-11-10 21:41:34 -05:00
19 changed files with 158 additions and 54 deletions

View File

@@ -28,12 +28,34 @@ The following people in no particular order also helped with the project:
- [sinefuse](https://github.com/sinefuse) - [sinefuse](https://github.com/sinefuse)
- [2Tie](https://github.com/2Tie) - [2Tie](https://github.com/2Tie)
- [nightmareci](https://github.com/nightmareci) - [nightmareci](https://github.com/nightmareci)
- [MyPasswordIsWeak](https://github.com/MyPasswordIsWeak)
- [Dr Ocelot](https://github.com/Dr-Ocelot)
![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png) ![Cambridge Logo](https://cdn.discordapp.com/attachments/625496179433668635/763363717730664458/Icon_2.png)
Playing the game Playing the game
---------------- ----------------
### Windows
You do not need LÖVE on Windows, as it comes bundled with the program.
To get the stable release, simply download the ZIP in the latest release. All assets needed are bundled with the executable.
If you want the bleeding edge version, or want mod pack support, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
dist\windows\love.exe .
Alternatively, if you're on a 32-bit system, run this instead:
dist\win32\love.exe .
32-bit systems do not support rich presence integration.
Then, check the mod pack section at the bottom of this page.
### macOS, Linux ### macOS, Linux
If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!** If you haven't already, install `love` with your favourite package manager (Homebrew on macOS, your system's default on Linux). **Make sure you're using LÖVE 11, because it won't work with earlier versions!**
@@ -50,25 +72,11 @@ Then, navigate to the root directory that you just cloned, and type:
It should run automatically! It should run automatically!
### Windows
You do not need LÖVE on Windows, as it comes bundled with the program. Download the source code ZIP in the latest release, or if you want the bleeding edge version, download [this](https://github.com/SashLilac/cambridge/archive/master.zip).
Extract the ZIP, open a Command Prompt at the folder you extracted Cambridge to, then run this command:
dist\windows\love.exe .
Alternatively, if you're on a 32-bit system, run this instead:
dist\win32\love.exe .
32-bit systems do not support rich presence integration.
## Installing modpacks ## Installing modpacks
Simply drag your mode, ruleset, and randomizer Lua files into their respective directory, and they should appear automatically. Simply drag your mode, ruleset, and randomizer Lua files into their respective directory, and they should appear automatically.
Alternatively, install [this](https://files.catbox.moe/66td2i.zip) mod pack to get a taste of the mod potential. Alternatively, install [this](https://github.com/SashLilac/cambridge-modpack) mod pack to get a taste of the mod potential.
License License
------- -------

View File

@@ -171,6 +171,20 @@ function Grid:applyFourWide()
end end
end end
function Grid:applyCeiling(lines)
for row = 1, lines do
for col = 1, 9 do
self.grid[row][col] = block
end
end
end
function Grid:clearSpecificRow(row)
for col = 1, 10 do
self.grid[row][col] = empty
end
end
function Grid:applyPiece(piece) function Grid:applyPiece(piece)
if piece.big then if piece.big then
self:applyBigPiece(piece) self:applyBigPiece(piece)
@@ -216,6 +230,15 @@ function Grid:checkForBravo(cleared_row_count)
return true return true
end end
function Grid:checkStackHeight()
for i = 0, 23 do
for j = 0, 9 do
if self:isOccupied(j, i) then return 24 - i end
end
end
return 0
end
function Grid:checkSecretGrade() function Grid:checkSecretGrade()
local sgrade = 0 local sgrade = 0
for i=23,5,-1 do for i=23,5,-1 do
@@ -296,7 +319,32 @@ function Grid:draw()
end end
end end
function Grid:drawInvisible(opacity_function, garbage_opacity_function) function Grid:drawOutline()
for y = 5, 24 do
for x = 1, 10 do
if self.grid[y][x] ~= empty then
love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1)
if y > 1 and self.grid[y-1][x] == empty then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end
if y < 24 and self.grid[y+1][x] == empty then
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
end
if x > 1 and self.grid[y][x-1] == empty then
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
end
if x < 10 and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end
end
end
end
end
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
lock_flash = lock_flash == nil and true or lock_flash
brightness = brightness == nil and 0.5 or brightness
for y = 5, 24 do for y = 5, 24 do
for x = 1, 10 do for x = 1, 10 do
if self.grid[y][x] ~= empty then if self.grid[y][x] ~= empty then
@@ -307,8 +355,9 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function)
else else
opacity = opacity_function(self.grid_age[y][x]) opacity = opacity_function(self.grid_age[y][x])
end end
love.graphics.setColor(0.5, 0.5, 0.5, opacity) love.graphics.setColor(brightness, brightness, brightness, opacity)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16) love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
if lock_flash then
if opacity > 0 and self.grid[y][x].colour ~= "X" then if opacity > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64) love.graphics.setColor(0.64, 0.64, 0.64)
love.graphics.setLineWidth(1) love.graphics.setLineWidth(1)
@@ -329,5 +378,6 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function)
end end
end end
end end
end
return Grid return Grid

View File

@@ -27,6 +27,7 @@ function MarathonA2Game:new()
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = false self.lock_drop = false
self.lock_hard_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
@@ -124,7 +125,8 @@ function MarathonA2Game:onLineClear(cleared_row_count)
self.grid:clear() self.grid:clear()
self.roll_frames = -150 self.roll_frames = -150
end end
if self.level >= 900 then self.lock_drop = true end self.lock_drop = self.level >= 900
self.lock_hard_drop = self.level >= 900
end end
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
@@ -248,7 +250,7 @@ end
function MarathonA2Game:drawGrid(ruleset) function MarathonA2Game:drawGrid(ruleset)
if self.clear and not (self.completed or self.game_over) then if self.clear and not (self.completed or self.game_over) then
self.grid:drawInvisible(self.rollOpacityFunction) self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
else else
self.grid:draw() self.grid:draw()
if self.piece ~= nil and self.level < 100 then if self.piece ~= nil and self.level < 100 then

View File

@@ -43,6 +43,8 @@ function GameMode:new()
self.draw_section_times = false self.draw_section_times = false
self.draw_secondary_section_times = false self.draw_secondary_section_times = false
self.big_mode = false self.big_mode = false
self.irs = true
self.ihs = true
self.rpc_details = "In game" self.rpc_details = "In game"
-- variables related to configurable parameters -- variables related to configurable parameters
self.drop_locked = false self.drop_locked = false
@@ -105,7 +107,7 @@ function GameMode:update(inputs, ruleset)
self:whilePieceActive() self:whilePieceActive()
local gravity = self:getGravity() local gravity = self:getGravity()
if self.enable_hold and inputs["hold"] == true and self.held == false then if self.enable_hold and inputs["hold"] == true and self.held == false and self.prev_inputs["hold"] == false then
self:hold(inputs, ruleset) self:hold(inputs, ruleset)
self.prev_inputs = inputs self.prev_inputs = inputs
return return
@@ -277,6 +279,13 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end end
elseif self.are > 0 then elseif self.are > 0 then
self.are = self.are - 1 self.are = self.are - 1
if ruleset.are_cancel and
(inputs["left"] or inputs["right"] or
inputs["rotate_left"] or inputs["rotate_left2"] or
inputs["rotate_right"] or inputs["rotate_right2"] or
inputs["rotate_180"]) then
self.are = 0
end
if self.are == 0 then if self.are == 0 then
self:initializeOrHold(inputs, ruleset) self:initializeOrHold(inputs, ruleset)
end end
@@ -284,7 +293,7 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
end end
function GameMode:initializeOrHold(inputs, ruleset) function GameMode:initializeOrHold(inputs, ruleset)
if self.enable_hold and inputs["hold"] == true then if self.ihs and self.enable_hold and inputs["hold"] == true then
self:hold(inputs, ruleset) self:hold(inputs, ruleset)
else else
self:initializeNextPiece(inputs, ruleset, self.next_queue[1]) self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
@@ -324,7 +333,8 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
inputs, piece_data, self.grid, gravity, inputs, piece_data, self.grid, gravity,
self.prev_inputs, self.move, self.prev_inputs, self.move,
self:getLockDelay(), self:getDropSpeed(), self:getLockDelay(), self:getDropSpeed(),
self.lock_drop, self.lock_hard_drop, self.big_mode self.lock_drop, self.lock_hard_drop, self.big_mode,
self.irs
) )
if self.lock_drop then if self.lock_drop then
self.drop_locked = true self.drop_locked = true

View File

@@ -144,6 +144,7 @@ function Marathon2020Game:advanceOneFrame()
if self.roll_frames < 0 then if self.roll_frames < 0 then
return false return false
elseif self.roll_frames > 4000 then elseif self.roll_frames > 4000 then
if self.grade >= 30 and self.section_cool_count >= 20 then self.grade = 31 end
self.completed = true self.completed = true
end end
elseif self.ready_frames == 0 then elseif self.ready_frames == 0 then
@@ -248,6 +249,7 @@ function Marathon2020Game:updateGrade(cleared_lines)
end end
function Marathon2020Game:getTotalGrade() function Marathon2020Game:getTotalGrade()
if self.grade + self.section_cool_count > 50 then return "GM" end
return self.grade + self.section_cool_count return self.grade + self.section_cool_count
end end
@@ -392,7 +394,6 @@ Marathon2020Game.mRollOpacityFunction = function(age)
end end
function Marathon2020Game:qualifiesForMRoll() function Marathon2020Game:qualifiesForMRoll()
return false -- until I actually have grading working
--[[ --[[
GM-roll requirements GM-roll requirements
@@ -403,6 +404,8 @@ You qualify for the GM roll if you:
- in less than 13:30.00 total. - in less than 13:30.00 total.
]]-- ]]--
return self.level >= 2020 and self:getTotalGrade() == 50 and self.frames <= frameTime(13,30)
end end
function Marathon2020Game:drawGrid() function Marathon2020Game:drawGrid()

View File

@@ -34,6 +34,7 @@ function MarathonA2Game:new()
} }
self.lock_drop = false self.lock_drop = false
self.lock_hard_drop = false
self.enable_hold = false self.enable_hold = false
self.next_queue_length = 1 self.next_queue_length = 1
end end
@@ -151,7 +152,8 @@ function MarathonA2Game:onLineClear(cleared_row_count)
if self:qualifiesForMRoll() then self.grade = 32 end if self:qualifiesForMRoll() then self.grade = 32 end
self.roll_frames = -150 self.roll_frames = -150
end end
if self.level >= 900 then self.lock_drop = true end self.lock_drop = self.level >= 900
self.lock_hard_drop = self.level >= 900
end end
function MarathonA2Game:updateSectionTimes(old_level, new_level) function MarathonA2Game:updateSectionTimes(old_level, new_level)
@@ -308,12 +310,12 @@ MarathonA2Game.mRollOpacityFunction = function(age)
else return 1 - age / 4 end else return 1 - age / 4 end
end end
function MarathonA2Game:drawGrid(ruleset) function MarathonA2Game:drawGrid()
if self.clear and not (self.completed or self.game_over) then if self.clear and not (self.completed or self.game_over) then
if self:qualifiesForMRoll() then if self:qualifiesForMRoll() then
self.grid:drawInvisible(self.mRollOpacityFunction) self.grid:drawInvisible(self.mRollOpacityFunction, nil, false)
else else
self.grid:drawInvisible(self.rollOpacityFunction) self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
end end
else else
self.grid:draw() self.grid:draw()

View File

@@ -231,8 +231,8 @@ function MarathonA3Game:updateSectionTimes(old_level, new_level)
end end
function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines) function MarathonA3Game:updateScore(level, drop_bonus, cleared_lines)
if not self.clear then
self:updateGrade(cleared_lines) self:updateGrade(cleared_lines)
if not self.clear then
if cleared_lines > 0 then if cleared_lines > 0 then
self.combo = self.combo + (cleared_lines - 1) * 2 self.combo = self.combo + (cleared_lines - 1) * 2
self.score = self.score + ( self.score = self.score + (

View File

@@ -24,6 +24,7 @@ function MarathonAX4Game:new()
self.section_clear = false self.section_clear = false
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
end end

View File

@@ -15,6 +15,7 @@ function PhantomManiaGame:new()
PhantomManiaGame.super:new() PhantomManiaGame.super:new()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.next_queue_length = 1 self.next_queue_length = 1
self.SGnames = { self.SGnames = {
@@ -138,7 +139,7 @@ end
function PhantomManiaGame:drawGrid() function PhantomManiaGame:drawGrid()
if not (self.game_over or self.clear) then if not (self.game_over or self.clear) then
self.grid:drawInvisible(self.rollOpacityFunction) self.grid:drawInvisible(self.rollOpacityFunction, nil, false)
else else
self.grid:draw() self.grid:draw()
end end

View File

@@ -35,6 +35,7 @@ function PhantomMania2Game:new()
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3

View File

@@ -0,0 +1,22 @@
local PhantomManiaGame = require 'tetris.modes.phantom_mania'
local PhantomManiaNGame = PhantomManiaGame:extend()
PhantomManiaNGame.name = "Phantom Mania N"
PhantomManiaNGame.hash = "PhantomManiaN"
PhantomManiaNGame.tagline = "The old mode from Nullpomino, for Ti-ARS and SRS support."
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
end
return PhantomManiaNGame

View File

@@ -27,6 +27,7 @@ function Survival2020Game:new()
self.randomizer = History6RollsRandomizer() self.randomizer = History6RollsRandomizer()
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3
end end

View File

@@ -27,6 +27,7 @@ function SurvivalA2Game:new()
} }
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
end end
function SurvivalA2Game:getARE() function SurvivalA2Game:getARE()

View File

@@ -29,6 +29,7 @@ function SurvivalA3Game:new()
} }
self.lock_drop = true self.lock_drop = true
self.lock_hard_drop = true
self.enable_hold = true self.enable_hold = true
self.next_queue_length = 3 self.next_queue_length = 3

View File

@@ -112,9 +112,9 @@ end
function ARS:get180RotationValue() function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then if config.gamesettings.world_reverse == 3 then
return 3
else
return 1 return 1
else
return 3
end end
end end

View File

@@ -187,9 +187,9 @@ end
function ARS:get180RotationValue() function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then if config.gamesettings.world_reverse == 3 then
return 3
else
return 1 return 1
else
return 3
end end
end end

View File

@@ -174,9 +174,9 @@ end
function ARS:get180RotationValue() function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then if config.gamesettings.world_reverse == 3 then
return 3
else
return 1 return 1
else
return 3
end end
end end

View File

@@ -153,9 +153,9 @@ end
function ARS:get180RotationValue() function ARS:get180RotationValue()
if config.gamesettings.world_reverse == 3 then if config.gamesettings.world_reverse == 3 then
return 3
else
return 1 return 1
else
return 3
end end
end end

View File

@@ -21,6 +21,7 @@ Ruleset.softdrop_lock = true
Ruleset.harddrop_lock = false Ruleset.harddrop_lock = false
Ruleset.enable_IRS_wallkicks = false Ruleset.enable_IRS_wallkicks = false
Ruleset.are_cancel = false
-- Component functions. -- Component functions.
@@ -180,7 +181,7 @@ function Ruleset:getDefaultOrientation() return 1 end
function Ruleset:initializePiece( function Ruleset:initializePiece(
inputs, data, grid, gravity, prev_inputs, inputs, data, grid, gravity, prev_inputs,
move, lock_delay, drop_speed, move, lock_delay, drop_speed,
drop_locked, hard_drop_locked, big drop_locked, hard_drop_locked, big, irs
) )
local spawn_positions local spawn_positions
if big then if big then
@@ -196,7 +197,7 @@ function Ruleset:initializePiece(
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big) }, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
self:onPieceCreate(piece) self:onPieceCreate(piece)
self:rotatePiece(inputs, piece, grid, {}, true) if irs then self:rotatePiece(inputs, piece, grid, {}, true) end
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked) self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
return piece return piece
end end