449 lines
12 KiB
Lua
449 lines
12 KiB
Lua
--[[
|
|
Blitz Mode
|
|
Design by MattMayuga
|
|
Spaghetti code by terpyderp
|
|
Music from Bejeweled Twist
|
|
HSV to RGB code copied from https://gist.github.com/GigsD4X/8513963 because I'm lazy and stupid lol.
|
|
]]--
|
|
|
|
require 'funcs'
|
|
|
|
local GameMode = require 'tetris.modes.gamemode'
|
|
local Piece = require 'tetris.components.piece'
|
|
|
|
local Bag7Randomiser = require 'tetris.randomizers.bag7noSZOstart'
|
|
|
|
local Blitz = GameMode:extend()
|
|
|
|
sounds.blitz = {
|
|
multi_up = love.audio.newSource("res/se/multiplier_up.wav", "static")
|
|
}
|
|
|
|
bgm.blitz = {
|
|
blitz = love.audio.newSource("res/bgm/blitz.mp3", "stream"),
|
|
}
|
|
|
|
Blitz.name = "Blitz"
|
|
Blitz.hash = "Blitz"
|
|
Blitz.tagline = "How many points can you get in 5 minutes? Do cool tricks, get All Clears, increase your score multiplier and bring your best game to reach higher speed levels and gain more points in this action-packed mode!"
|
|
|
|
local scoreTable = {
|
|
250, -- Single
|
|
750,
|
|
1250,
|
|
2000, -- Quadruple
|
|
3750, -- Quintuple
|
|
1000, -- T-Spin Zero
|
|
2000,
|
|
3000,
|
|
4000, -- T-Spin Triple
|
|
250, -- T-Spin Mini Zero
|
|
750,
|
|
1000, -- T-Spin Mini Double
|
|
2000, -- All Clear Single
|
|
3000,
|
|
4500,
|
|
5000,
|
|
6000, -- All Clear Quintuple
|
|
8000, -- B2B AC Quadruple
|
|
9000 -- B2B AC Quintuple
|
|
}
|
|
|
|
local chainActions = {
|
|
3, 4, 6, 8, 10, 10, 10, 10, 10, 10, 10
|
|
}
|
|
|
|
local SRSTPiece = {
|
|
{ {x=0, y=0}, {x=-1, y=0}, {x=1, y=0}, {x=0, y=-1} },
|
|
{ {x=0, y=0}, {x=0, y=-1}, {x=0, y=1}, {x=1, y=0} },
|
|
{ {x=0, y=0}, {x=1, y=0}, {x=-1, y=0}, {x=0, y=1} },
|
|
{ {x=0, y=0}, {x=0, y=1}, {x=0, y=-1}, {x=-1, y=0} },
|
|
}
|
|
local ARSTPiece = {
|
|
{ {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} },
|
|
}
|
|
|
|
function Blitz:new()
|
|
Blitz.super:new()
|
|
|
|
self.lines = 0
|
|
self.frames = 18000
|
|
self.pieces = 0
|
|
self.roll_frames = 0
|
|
self.upstacked = false
|
|
self.activeTime = 0
|
|
self.framesButBackwards = 0
|
|
|
|
-- blitz mode variables
|
|
self.score = 0
|
|
self.b2b = 0
|
|
self.b2bMulti = 1
|
|
self.multi = 1
|
|
self.chain = 0
|
|
self.lastLineCount = 4
|
|
self.lastPieceTime = 0
|
|
self.rainbowMode = 0 -- 0 = not started, 1 = active, 2 = finished
|
|
self.tSpin = 0 -- 0 = none, 1 = normal, 2 = mini
|
|
|
|
self.randomizer = Bag7Randomiser()
|
|
self.lock_drop = true
|
|
self.lock_hard_drop = true
|
|
self.instant_hard_drop = true
|
|
self.instant_soft_drop = false
|
|
self.enable_hold = true
|
|
self.next_queue_length = 5
|
|
self.immobile_spin_bonus = true
|
|
end
|
|
|
|
|
|
function Blitz:getDropSpeed() return 20 end
|
|
function Blitz:getARR() return config.arr end
|
|
function Blitz:getARE() return 0 end
|
|
function Blitz:getLineARE() return self:getARE() end
|
|
function Blitz:getDasLimit() return config.das end
|
|
function Blitz:getLineClearDelay() return 0 end
|
|
function Blitz:getLockDelay() return 30 end
|
|
function Blitz:getGravity() return 1/64 end
|
|
function Blitz:getDasCutDelay() return config.dcd end
|
|
function Blitz:getBackground() return 3 end
|
|
|
|
|
|
function Blitz:advanceOneFrame()
|
|
if self.ready_frames == 0 then
|
|
if self.frames == 18000 then
|
|
switchBGM("blitz", "blitz")
|
|
end
|
|
end
|
|
|
|
if self.clear then
|
|
self.roll_frames = self.roll_frames + 1
|
|
if self.roll_frames > 360 then
|
|
self.completed = true
|
|
end
|
|
return false
|
|
elseif self.ready_frames == 0 then
|
|
self.frames = self.frames - 1
|
|
if self.frames <= 0 then
|
|
self.clear = true
|
|
end
|
|
end
|
|
self.framesButBackwards = self.framesButBackwards + 1
|
|
return true
|
|
end
|
|
|
|
|
|
function Blitz:onPieceLock(piece, cleared_row_count)
|
|
self.super:onPieceLock()
|
|
self.pieces = self.pieces + 1
|
|
allClear = self.grid:checkForBravo(cleared_row_count)
|
|
|
|
|
|
-- detect what kind of T piece the ruleset has (either 0-SRS, 1-ARS, or 2-idk) (pain)
|
|
if piece.shape == "T" then
|
|
SRS = true
|
|
for i,v in ipairs(self.ruleset.block_offsets.T) do -- for every rotation of the T
|
|
for ii,vv in ipairs(v) do -- for every block in the T
|
|
if #(v) == 4 then
|
|
if vv.x ~= SRSTPiece[i][ii].x and vv.y ~= SRSTPiece[i][ii].y then
|
|
SRS = false
|
|
break
|
|
end
|
|
else
|
|
SRS = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if SRS then
|
|
self.rulesetType = 0
|
|
else
|
|
ARS = true
|
|
for i,v in ipairs(self.ruleset.block_offsets.T) do -- for every rotation of the T
|
|
for ii,vv in ipairs(v) do -- for every block in the T
|
|
if #(v) == 4 then
|
|
if vv.x ~= ARSTPiece[i][ii].x and vv.y ~= ARSTPiece[i][ii].y then
|
|
ARS = false
|
|
break
|
|
end
|
|
else
|
|
ARS = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if ARS then
|
|
self.rulesetType = 1
|
|
else
|
|
self.rulesetType = 2
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 4-corner T-Spin detection (also pain)
|
|
if self.rulesetType == 2 then -- just be lazy and do immobile if it's not SRS or ARS
|
|
if self.piece.spin and self.piece.shape == "T" then
|
|
self.tSpin = 1
|
|
else
|
|
self.tSpin = 0
|
|
end
|
|
else
|
|
if self.rulesetType == 0 then tempYOffset = 0
|
|
else tempYOffset = -1
|
|
end
|
|
|
|
if piece.shape == "T" and piece.last_rotated then
|
|
topCount = 0
|
|
bottomCount = 0
|
|
if self.grid:isOccupied(piece.position.x+1, piece.position.y+1) then
|
|
if piece.rotation <= 1 then bottomCount = bottomCount + 1
|
|
else topCount = topCount + 1
|
|
end
|
|
end
|
|
if self.grid:isOccupied(piece.position.x+1, piece.position.y-1+tempYOffset) then
|
|
if piece.rotation <= 1 then topCount = topCount + 1
|
|
else bottomCount = bottomCount + 1
|
|
end
|
|
end
|
|
if self.grid:isOccupied(piece.position.x-1, piece.position.y+1+tempYOffset) then
|
|
if piece.rotation == 1 or piece.rotation == 2 then topCount = topCount + 1
|
|
else bottomCount = bottomCount + 1
|
|
end
|
|
end
|
|
if self.grid:isOccupied(piece.position.x-1, piece.position.y-1+tempYOffset) then
|
|
if piece.rotation == 1 or piece.rotation == 2 then bottomCount = bottomCount + 1
|
|
else topCount = topCount + 1
|
|
end
|
|
end
|
|
if topCount == 2 and bottomCount >= 1 then
|
|
self.tSpin = 1 -- normal
|
|
elseif topCount == 1 and bottomCount == 2 then
|
|
self.tSpin = 2 -- mini
|
|
else
|
|
self.tSpin = 0 -- none
|
|
end
|
|
else
|
|
self.tSpin = 0
|
|
end
|
|
end
|
|
|
|
-- updates the multiplier for back to back
|
|
if self.b2b > 0 then self.b2bMulti = 1.5
|
|
else self.b2bMulti = 1
|
|
end
|
|
|
|
-- updates the score
|
|
-- if it's a T spin
|
|
if self.tSpin == 1 then
|
|
self.score = self.score + scoreTable[cleared_row_count+6] * self.b2bMulti * self.multi
|
|
-- if it's a T spin mini
|
|
elseif self.tSpin == 2 then
|
|
self.score = self.score + scoreTable[cleared_row_count+10] * self.b2bMulti * self.multi
|
|
-- if it's an all clear
|
|
elseif self.grid:checkForBravo(cleared_row_count) then
|
|
if self.b2b > 0 and cleared_row_count >= 4 then
|
|
self.score = self.score + scoreTable[cleared_row_count+14] * self.multi
|
|
else
|
|
self.score = self.score + scoreTable[cleared_row_count+12] * self.multi
|
|
end
|
|
-- if it's a quad or quin
|
|
elseif cleared_row_count >= 4 then
|
|
self.score = self.score + scoreTable[cleared_row_count] * self.b2bMulti * self.multi
|
|
-- if it's none of the above and it clears at least one line
|
|
elseif cleared_row_count > 0 then
|
|
self.score = self.score + scoreTable[cleared_row_count] * self.multi
|
|
end
|
|
|
|
-- raises the chain count if necessary
|
|
-- if it's an all clear
|
|
if allClear then
|
|
self.chain = self.chain + 12
|
|
-- if at least 4 lines are cleared or it's a T-Spin
|
|
elseif self.tSpin ~= 0 or cleared_row_count >= 4 then
|
|
if self.b2b > 10 then
|
|
self.chain = self.chain + 2
|
|
else
|
|
self.chain = self.chain + 1
|
|
end
|
|
-- if 3 lines are cleared
|
|
elseif cleared_row_count >= 3 then
|
|
self.chain = self.chain + 1
|
|
end
|
|
|
|
-- decreases the multiplier or chain meter if necessary
|
|
if self.lastLineCount < 3 and (cleared_row_count < 3 and cleared_row_count ~= 0) and not allClear then
|
|
if self.chain < 1 then
|
|
if self.multi > 1 then
|
|
self.multi = self.multi - 1
|
|
self.rainbowMode = 0
|
|
end
|
|
else
|
|
if self.rainbowMode == 2 then
|
|
self.rainbowMode = 0
|
|
self.multi = 9
|
|
end
|
|
self.chain = 0
|
|
end
|
|
elseif self.lastPieceTime >= 180 and self.activeTime >= 180 and self.multi > 1 then
|
|
self.multi = self.multi - 1
|
|
end
|
|
|
|
-- increases the multiplier if necessary
|
|
if self.multi < 10 then
|
|
while self.chain >= chainActions[self.multi] do
|
|
self.chain = self.chain - chainActions[self.multi]
|
|
self.multi = self.multi + 1
|
|
playSE("blitz", "multi_up")
|
|
end
|
|
elseif self.rainbowMode == 0 then
|
|
if self.chain >= chainActions[10] then
|
|
self.rainbowMode = 1
|
|
self.chain = self.chain - chainActions[10]
|
|
playSE("blitz", "multi_up")
|
|
end
|
|
elseif self.rainbowMode == 1 then
|
|
if self.chain >= chainActions[11] then
|
|
self.score = self.score + 300000
|
|
self.chain = 10
|
|
self.rainbowMode = 2
|
|
playSE("blitz", "multi_up")
|
|
end
|
|
end
|
|
|
|
-- updates back to back
|
|
if self.tSpin ~= 0 or cleared_row_count >= 4 then
|
|
self.b2b = self.b2b + 1
|
|
elseif cleared_row_count > 0 and cleared_row_count < 4 then
|
|
self.b2b = 0
|
|
end
|
|
|
|
-- print("chain: " .. tostring(self.chain) .. " multiplier: " .. tostring(self.multi) .. " rainbow mode: " .. tostring(self.rainbowMode) .. " lastLineCount: " .. tostring(self.lastLineCount))
|
|
|
|
|
|
-- update some other stuff
|
|
self.lastPieceTime = self.activeTime
|
|
self.activeTime = 0
|
|
if self.tSpin ~= 0 then
|
|
self.lastLineCount = 4 -- if it works it's not stupid right?
|
|
elseif cleared_row_count ~= 0 then
|
|
self.lastLineCount = cleared_row_count
|
|
end
|
|
if self.rainbowMode == 2 then
|
|
self.chain = chainActions[11]
|
|
end
|
|
end
|
|
|
|
|
|
function Blitz:onLineClear(cleared_row_count)
|
|
if not self.clear then
|
|
self.lines = self.lines + cleared_row_count
|
|
end
|
|
end
|
|
|
|
|
|
function Blitz:drawGrid(ruleset)
|
|
self.grid:draw()
|
|
if self.piece ~= nil then
|
|
self:drawGhostPiece(ruleset)
|
|
end
|
|
end
|
|
|
|
|
|
function Blitz:getHighscoreData()
|
|
return {
|
|
level = self.level,
|
|
frames = self.frames,
|
|
}
|
|
end
|
|
|
|
|
|
function Blitz:drawScoringInfo()
|
|
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
|
|
if config["side_next"] then
|
|
love.graphics.printf("NEXT", 240, 72, 40, "left")
|
|
else
|
|
love.graphics.printf("NEXT", 64, 40, 40, "left")
|
|
end
|
|
|
|
local text_x = config["side_next"] and 316 or 240
|
|
|
|
love.graphics.setFont(font_3x5_2)
|
|
love.graphics.printf("SCORE", text_x, 72, 80, "left")
|
|
love.graphics.printf("PPS", text_x, 132, 80, "left")
|
|
love.graphics.printf("LINES", text_x, 192, 40, "left")
|
|
|
|
love.graphics.setFont(font_3x5_3)
|
|
love.graphics.printf(self.score, text_x, 92, 80, "left")
|
|
love.graphics.printf(string.format("%.04f", self.pieces / math.max(1, self.framesButBackwards) * 60), text_x, 152, 80, "left")
|
|
love.graphics.printf(math.max(0, self.lines), text_x, 212, 40, "left")
|
|
|
|
-- draw chain meter
|
|
if self.rainbowMode == 1 then
|
|
self.red, self.green, self.blue = Blitz:HSVToRGB(((-self.frames+180000)*2)%360, 1, 1)
|
|
else
|
|
self.red, self.green, self.blue = Blitz:HSVToRGB((-self.multi*36)+395, 1, 1)
|
|
end
|
|
love.graphics.setColor(self.red/4, self.blue/4, self.green/4)
|
|
love.graphics.rectangle("fill", 240, 382, 160, 24)
|
|
love.graphics.setColor(self.red, self.blue, self.green)
|
|
love.graphics.rectangle("fill", 240, 382, (self.chain/chainActions[self.multi])*160, 24)
|
|
love.graphics.setColor(0, 0, 0)
|
|
love.graphics.setLineWidth(4)
|
|
love.graphics.rectangle("line", 240, 382, 160, 24)
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
love.graphics.setFont(font_3x5_2)
|
|
love.graphics.printf(tostring(self.chain) .. " / " .. tostring(chainActions[self.multi]), 240, 412, 160, "center")
|
|
|
|
-- draw multiplier
|
|
love.graphics.setFont(font_3x5_3)
|
|
love.graphics.printf("MULTI: X" .. tostring(self.multi), 240, 352, 400, "left")
|
|
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
end
|
|
|
|
|
|
function Blitz:whilePieceActive()
|
|
self.activeTime = self.activeTime + 1
|
|
end
|
|
|
|
|
|
-- copied from https://gist.github.com/GigsD4X/8513963
|
|
function Blitz:HSVToRGB( hue, saturation, value )
|
|
-- Returns the RGB equivalent of the given HSV-defined color
|
|
-- (adapted from some code found around the web)
|
|
|
|
-- If it's achromatic, just return the value
|
|
if saturation == 0 then
|
|
return value, value, value;
|
|
end;
|
|
|
|
-- Get the hue sector
|
|
local hue_sector = math.floor( hue / 60 );
|
|
local hue_sector_offset = ( hue / 60 ) - hue_sector;
|
|
|
|
local p = value * ( 1 - saturation );
|
|
local q = value * ( 1 - saturation * hue_sector_offset );
|
|
local t = value * ( 1 - saturation * ( 1 - hue_sector_offset ) );
|
|
|
|
if hue_sector == 0 then
|
|
return value, t, p;
|
|
elseif hue_sector == 1 then
|
|
return q, value, p;
|
|
elseif hue_sector == 2 then
|
|
return p, value, t;
|
|
elseif hue_sector == 3 then
|
|
return p, q, value;
|
|
elseif hue_sector == 4 then
|
|
return t, p, value;
|
|
elseif hue_sector == 5 then
|
|
return value, p, q;
|
|
end;
|
|
end;
|
|
|
|
|
|
return Blitz
|