require 'funcs' local GameMode = require 'tetris.modes.gamemode' local Piece = require 'tetris.components.piece' local Bag7NoSZOStartRandomizer = require 'tetris.randomizers.bag7noSZOstart' local HighStackerGame = GameMode:extend() HighStackerGame.name = "High Stacker" HighStackerGame.hash = "HighStacker" HighStackerGame.tagline = "Play risky on the higher fields and don't be a chicken!" --Official Release v1.0 --Concept & Coding by Xx_Henry_xX (Xx_Henry_xX#0296) --Code mostly & shamelessly stolen from... --Marathon A2 / A3 --Survival 2020 --[[ changelog Official Release v1.0 -2'-ALL ending appearing on other ALLs fixed -transition bug is fixed AT LAST!!!!!!!!!!!!! Official Release v0.2 -null piece bug fixed -some pointless bool == bool stuffs edited Official Release v0.1 -total delay is now ura loop 2 exclusive -edited total delay values -end of game bonus details added -2'-ALL ending added -roll bgm added Discord Beta v2.0 -scoring rehaul (mostly on REN) -slot is gone -current section score & section cool requirement display added Discord Beta v1.2 -adjusted section cool cutoffs and ura 2nd loop cutoff -fixed end of game bonus being given endlessly (kudos 2 infinifen#1129) Discord Beta v1.1 -bugfixes on section score text color -end of game bonus added Discord Beta v1.0 -initial semi-public release ]] function HighStackerGame:new() HighStackerGame.super:new() self.roll_frames = 0 self.combo = 0 self.randomizer = Bag7NoSZOStartRandomizer() self.grade = 0 self.grade_points = 0 self.grade_point_decay_counter = 0 self.section_start_score = 0 self.section_scores = { [0] = 0 } self.loop = 0 --0 == loop 1 / 1 == omote / 2 == ura self.total_lines = 0 self.section_cools = 0 self.cool_timer = 0 self.ground_touched = false self.total_delay = 300 self.show_loop = false self.actually_cue_the_rolls = false self.ren_on = false self.line_details = { } self.line_details_second = { } self.detail_timer = 0 self.ending = 0 self.clear_bonus_given = false --debug --[[ self.level = 499 self.section_cools = 4 self.score = 50000000 self.section_scores = { [0] = 10000000, [1] = 10000000, [2] = 10000000, [3] = 10000000 } ]] self.additive_gravity = false self.lock_drop = false self.lock_hard_drop = false self.enable_hold = true self.next_queue_length = 3 end function HighStackerGame:getARE() if self.level < 700 then return 27 elseif self.level < 800 then return 18 else return 14 end end function HighStackerGame:getLineARE() if self.level < 600 then return 27 elseif self.level < 700 then return 18 elseif self.level < 800 then return 14 else return 8 end end function HighStackerGame:getDasLimit() if self.level < 500 then return 15 elseif self.level < 900 then return 9 else return 7 end end function HighStackerGame:getLineClearDelay() if self.level < 500 then return 40 elseif self.level < 600 then return 25 elseif self.level < 700 then return 16 elseif self.level < 800 then return 12 else return 6 end end function HighStackerGame:getLockDelay() if self.level < 900 then return 30 else return 17 end end function HighStackerGame: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 HighStackerGame:getTotalDelay() if self.loop ~= 2 then return 300 elseif self.level < 530 then return 270 elseif self.level < 600 then return 240 elseif self.level < 700 then return 210 elseif self.level < 800 then return 180 elseif self.level < 900 then return 150 else return 120 end end function HighStackerGame:advanceOneFrame() if self.clear then self.roll_frames = self.roll_frames + 1 if not self.actually_cue_the_rolls then if self.roll_frames < -120 then return false elseif self.roll_frames >= 0 then if self.loop ~= 0 then self.clear = false self.ready_frames = 100 self.show_loop = true end return true end return false elseif self.roll_frames < 0 then if self.roll_frames + 1 == 0 then switchBGM("credit_roll", "gm3") end return false elseif self.roll_frames > 3701 then self.completed = true switchBGM(nil) end elseif self.ready_frames == 0 then self.frames = self.frames + 1 if self.piece ~= nil then if self.ground_touched and self.loop == 2 then self.total_delay = self.total_delay + 1 if self.total_delay >= self:getTotalDelay() and self.piece:isDropBlocked(self.grid) then self.piece.locked = true end elseif self.piece:isDropBlocked(self.grid) then self.ground_touched = true end end end if self.detail_timer > 0 then self.detail_timer = self.detail_timer - 1 end return true end function HighStackerGame:onPieceEnter() self.total_delay = 0 self.ground_touched = false if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 and not self.ren_on then self.level = self.level + 1 end end local line_count_multiplier = {1, 2, 4, 6} local line_position_score = { 15000, 15000, 15000, 15000, --lowest off-field row 15000, 10000, 9000, 8000, 7000, 6000, 5000, 4000, 3000, 2000, 1000, 900, 800, 700, 600, 500, 40, 30, 20, 10 } function HighStackerGame:updateScore(level, drop_bonus, cleared_lines) local ae, cleared_lines_table = self.grid:getClearedRowCount() self.total_lines = self.total_lines + cleared_lines if cleared_lines > 0 then self.detail_timer = 120 self.line_details = { } self.line_details_second = { } local basescore = self.combo local levelscore = self.level * 10 table.insert(self.line_details_second, levelscore) if basescore > 0 then table.insert(self.line_details_second, basescore) self.ren_on = true end if self.loop == 1 then levelscore = levelscore - 5000 end basescore = basescore + levelscore for key, pos in ipairs(cleared_lines_table) do basescore = basescore + line_position_score[pos] table.insert(self.line_details, line_position_score[pos]) end self.score = self.score + basescore * line_count_multiplier[cleared_lines] self.combo = basescore else self.combo = 0 self.ren_on = false end self.drop_bonus = 0 if self.clear then self.lines = self.lines + cleared_lines end --reminder that this is for roll lines end function HighStackerGame:onLineClear(cleared_row_count) if self.ren_on == false then self:updateSectionScores(self.level, self.level + cleared_row_count) self.level = math.min(self.level + cleared_row_count, 1000) end if self.level >= 500 and not self.clear and self.loop == 0 then self.level = 500 self.clear = true self.grid:clear() self.roll_frames = -150 if self:nextloopcheck() then self.roll_frames = self.roll_frames - 120 self.lcd = 0 self.are = 0 else self.actually_cue_the_rolls = true end elseif self.level == 1000 and not self.clear then self.clear = true self.grid:clear() self.actually_cue_the_rolls = true self.roll_frames = -150 end self.lock_drop = self.level >= 900 self.lock_hard_drop = self.level >= 900 end local lowest_section_cool_cutoff = 800000 function HighStackerGame:updateSectionScores(old_level, new_level) if self.clear or self.loop ~= 0 then return end if math.floor(old_level / 100) < math.floor(new_level / 100) or new_level >= 500 then -- record new section section_score = self.score - self.section_start_score self.section_scores[math.floor(old_level / 100)] = section_score self.section_start_score = self.score if self.loop == 0 then if section_score >= lowest_section_cool_cutoff + 50000 * (math.floor(old_level / 100) + self.section_cools) then self.section_cools = self.section_cools + 1 self.cool_timer = 300 end end end end function HighStackerGame:nextloopcheck() if not self.clear then return false end if self.section_cools >= 5 and self.score >= 6000000 then self.loop = 2 return true elseif self.section_cools >= 3 and self.score >= 4000000 then self.loop = 1 return true end return false end function HighStackerGame:getSkin() return (self.level >= 701 and self.loop == 2) and "bone" or "2tie" end function HighStackerGame:onGameComplete() if not self.clear_bonus_given then self.clear_bonus_given = true if self.loop == 2 then self.score = self.score + self.total_lines * 100 + self.lines * 1000 elseif self.loop == 1 then self.score = self.score + self.total_lines * 100 + self.lines * 500 else self.score = self.score + self.total_lines * 10 + self.lines * 100 end end self:onGameOver() end function HighStackerGame:onGameOver() switchBGM(nil) if not self.clear_bonus_given then love.graphics.setColor(1, 1, 1, 1) love.graphics.setFont(font_3x5_3) love.graphics.printf("GAME\nOVER", 64, 200, 160, "center") else if self.game_over_frames >= 300 then local alpha = 0 local animation_length = 120 if self.game_over_frames - 300 < animation_length then -- Show field for a bit, then fade out. alpha = math.pow(2048, (self.game_over_frames - 300)/animation_length - 1) else alpha = 1 end love.graphics.setColor(0, 0, 0, alpha) love.graphics.rectangle( "fill", 64, 80, 16 * self.grid.width, 16 * (self.grid.height - 4) ) end if self.game_over_frames >= 420 and loop == 2 then love.graphics.setColor(1, 1, 1, 1) love.graphics.setFont(font_3x5_2) love.graphics.printf( [[And so, the journey for the true end of this absurd scoring mode is over... From now on, aim for an even higher score. May fortune be with you. -Xx_Henry_xX]], 67, math.max(125, 1000 - self.game_over_frames), 160, "left") end end end HighStackerGame.rollOpacityFunction = function(age) if age < 240 then return 1 elseif age > 300 then return 0 else return 1 - (age - 240) / 60 end end HighStackerGame.mRollOpacityFunction = function(age) if age > 4 then return 0 else return 1 - age / 4 end end function HighStackerGame:drawGrid() if self.clear and not (self.completed or self.game_over) then if self.loop == 2 then self.grid:drawInvisible(self.mRollOpacityFunction, nil, false) elseif self.loop == 1 then self.grid:drawInvisible(self.rollOpacityFunction, nil, false) else self.grid:draw() end else self.grid:draw() if self.piece ~= nil then self:drawGhostPiece(ruleset) end end end function HighStackerGame:drawScoringInfo() love.graphics.setColor(1, 1, 1, 1) love.graphics.setFont(font_3x5_2) --y is dis debug text everywhere --[[ love.graphics.setFont(font_3x5_2) love.graphics.print( self.das.direction .. " " .. self.das.frames .. " " .. strTrueValues(self.prev_inputs) ) ]] love.graphics.printf("NEXT", 126, 6, 40, "left") love.graphics.printf("HOLD", 46, 6, 40, "left") --love.graphics.printf("SCORE", 240, 200, 40, "left") local vertpos = 320 if self.show_loop then vertpos = 290 end love.graphics.printf("LEVEL", 240, vertpos, 40, "left") love.graphics.setFont(font_3x5_3) if self.clear and not self.actually_cue_the_rolls and self.roll_frames >= -120 then if self.loop == 2 then love.graphics.printf("WELCOME TO THE SPECIAL ROUND", 64, 200, 160, "center") else love.graphics.printf("Let's try everything again", 64, 200, 160, "center") end end if self.clear_bonus_given then if self.loop == 0 then love.graphics.setFont(font_3x5_3) love.graphics.printf("ALL CLEAR!", 44, 100, 200, "center") love.graphics.setFont(font_3x5) love.graphics.printf("(probably)", 44, 125, 200, "center") love.graphics.setFont(font_3x5_2) love.graphics.printf("TOTAL LINES", 80, 240, 120, "left") love.graphics.printf(self.total_lines .. " x 10\n" .. (self.total_lines * 10), 80, 260, 120, "right") love.graphics.printf("ROLL LINES", 80, 320, 120, "left") love.graphics.printf(self.lines .. " x 100\n" .. (self.lines * 100), 80, 340, 120, "right") elseif self.loop == 1 then love.graphics.setFont(font_3x5_3) love.graphics.printf("ALL CLEAR!", 44, 100, 200, "center") love.graphics.setFont(font_3x5) love.graphics.printf("(probably)", 44, 125, 200, "center") love.graphics.setFont(font_3x5_2) love.graphics.printf("TOTAL LINES", 80, 240, 120, "left") love.graphics.printf(self.total_lines .. " x 100\n" .. (self.total_lines * 100), 80, 260, 120, "right") love.graphics.printf("ROLL LINES", 80, 320, 120, "left") love.graphics.printf(self.lines .. " x 500\n" .. (self.lines * 500), 80, 340, 120, "right") elseif self.game_over_frames < 300 then love.graphics.printf("CONGRATULATIONS!\nTRUE 2-ALL\nCLEAR!!!", 44, 100, 200, "center") love.graphics.setFont(font_3x5_2) love.graphics.printf("TOTAL LINES", 80, 240, 120, "left") love.graphics.printf(self.total_lines .. " x 100\n" .. (self.total_lines * 100), 80, 260, 120, "right") love.graphics.printf("ROLL LINES", 80, 320, 120, "left") love.graphics.printf(self.lines .. " x 1000\n" .. (self.lines * 1000), 80, 340, 120, "right") end end 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_8x11) love.graphics.setColor(1, 1, 1, 1) love.graphics.printf(self.score, 48, 420, 180, "right") love.graphics.setFont(font_3x5_3) if self.show_loop then if self.loop == 2 then love.graphics.printf("2'-", 240, 310, 40, "right") elseif self.loop == 1 then love.graphics.printf("2-", 240, 310, 40, "left") end end love.graphics.printf(self:getLevel(), 240, 340, 40, "right") love.graphics.printf(self:getSectionEndLevel(), 240, 370, 40, "right") love.graphics.setFont(font_3x5_2) love.graphics.printf(formatTime(self.frames), 500, 420, 180, "left") love.graphics.setColor(1, 1, 1, 1) if self.loop == 0 then local prevSectionTotal = 0 for i = 0, #self.section_scores do prevSectionTotal = prevSectionTotal + self.section_scores[i] end love.graphics.printf(self.score - prevSectionTotal, 240, 420, 70, "right") love.graphics.printf("/" .. lowest_section_cool_cutoff + 50000 * (math.floor(self.level / 100) + self.section_cools), 240, 436, 70, "right") end cool_counter = 0 for i = 0, #self.section_scores do love.graphics.setColor(1, 1, 1, 1) if self.section_scores[i] > lowest_section_cool_cutoff + 50000 * (i + cool_counter) then love.graphics.setColor(0, 1, 0, 1) cool_counter = cool_counter + 1 end love.graphics.printf(self.section_scores[i], 400, 80 + 16 * i, 180, "right") end love.graphics.setColor(1, 1, 1, 1) if self.detail_timer > 0 then for i = 1, #self.line_details do love.graphics.printf(self.line_details[i] .. "x" .. line_count_multiplier[#self.line_details], 240, 134 + 16 * i, 60, "right") end for i = 1, #self.line_details_second do love.graphics.printf(self.line_details_second[i] .. "x" .. line_count_multiplier[#self.line_details], 240, 196 + 16 * i, 60, "right") end end end function HighStackerGame:getHighscoreData() return { score = self.score, level = self.level, frames = self.frames, } end function HighStackerGame:getLevel() if self.loop ~=0 then return self.level - 500 end return self.level end function HighStackerGame:getSectionEndLevel() if self.loop ~=0 then return math.min(math.floor((self.level - 500) / 100 + 1) * 100, 500) end return math.min(math.floor(self.level / 100 + 1) * 100, 500) end function HighStackerGame:getBackground() return math.floor(self.level / 100) end return HighStackerGame