diff --git a/README.md b/README.md
index 1f7bd6e..d9af2c4 100644
--- a/README.md
+++ b/README.md
@@ -26,10 +26,10 @@ A modern Tetris implementation for Android, featuring smooth animations, respons
The game features a comprehensive scoring system:
#### Base Scores
-- Single line: 40 points
-- Double: 100 points
-- Triple: 300 points
-- Tetris (4 lines): 1200 points
+- Single line: 100 points
+- Double: 300 points
+- Triple: 500 points
+- Quad (4 lines): 1200 points
#### Multipliers
@@ -48,15 +48,15 @@ The game features a comprehensive scoring system:
- 4 combos: 2.5x
- 5+ combos: 3.0x
-3. **Back-to-Back Tetris**
- - 50% bonus (1.5x) for consecutive Tetris clears
- - Resets if a non-Tetris clear is performed
+3. **Back-to-Back Quad**
+ - 50% bonus (1.5x) for consecutive Quad clears
+ - Resets if a non-Quad clear is performed
4. **Perfect Clear**
- 2x for single line
- 3x for double
- 4x for triple
- - 5x for Tetris
+ - 5x for Quad
- Awarded when clearing lines without leaving blocks
5. **All Clear**
diff --git a/app/src/main/java/com/pixelmintdrop/MainActivity.kt b/app/src/main/java/com/pixelmintdrop/MainActivity.kt
index 5e9841b..f5e39e8 100644
--- a/app/src/main/java/com/pixelmintdrop/MainActivity.kt
+++ b/app/src/main/java/com/pixelmintdrop/MainActivity.kt
@@ -214,6 +214,7 @@ class MainActivity : AppCompatActivity(),
// Set up game view
gameView.setGameBoard(gameBoard)
gameView.setHaptics(gameHaptics)
+ gameBoard.setStatsManager(statsManager)
// Set up theme selector
themeSelector.onThemeSelected = { themeId: String ->
@@ -301,7 +302,16 @@ class MainActivity : AppCompatActivity(),
statsManager.updateSessionStats(finalScore, gameBoard.lines, piecesPlaced, timePlayedMs, currentLevel)
// Handle progression - XP earned, potential level up
- val xpGained = progressionManager.calculateGameXP(finalScore, gameBoard.lines, currentLevel, timePlayedMs, statsManager.getSessionTetrises(), 0)
+ val xpGained = progressionManager.calculateGameXP(
+ score = finalScore,
+ linesCleared = gameBoard.lines,
+ level = currentLevel,
+ gameTime = timePlayedMs,
+ tSpins = statsManager.getSessionTSpins(),
+ combos = statsManager.getSessionMaxCombo(),
+ quadCount = statsManager.getSessionQuads(),
+ perfectClearCount = statsManager.getSessionPerfectClears()
+ )
val newRewards = progressionManager.addXP(xpGained)
// Show progression screen if player earned XP
@@ -468,12 +478,14 @@ class MainActivity : AppCompatActivity(),
// Calculate XP earned
val xpGained = progressionManager.calculateGameXP(
- score = score,
- lines = gameBoard.lines,
+ score = statsManager.getSessionScore(),
+ linesCleared = statsManager.getSessionLines(),
level = currentLevel,
gameTime = gameTime,
- tetrisCount = statsManager.getSessionTetrises(),
- perfectClearCount = 0 // Implement perfect clear tracking if needed
+ tSpins = statsManager.getSessionTSpins(),
+ combos = statsManager.getSessionMaxCombo(),
+ quadCount = statsManager.getSessionQuads(),
+ perfectClearCount = statsManager.getSessionPerfectClears()
)
// Add XP and check for rewards
@@ -496,7 +508,7 @@ class MainActivity : AppCompatActivity(),
binding.sessionSinglesText.text = getString(R.string.singles, statsManager.getSessionSingles())
binding.sessionDoublesText.text = getString(R.string.doubles, statsManager.getSessionDoubles())
binding.sessionTriplesText.text = getString(R.string.triples, statsManager.getSessionTriples())
- binding.sessionTetrisesText.text = getString(R.string.tetrises, statsManager.getSessionTetrises())
+ binding.sessionQuadsText.text = getString(R.string.quads, statsManager.getSessionQuads())
// Flag to track if high score screen will be shown
var showingHighScore = false
@@ -911,7 +923,16 @@ class MainActivity : AppCompatActivity(),
statsManager.updateSessionStats(finalScore, gameBoard.lines, piecesPlaced, timePlayedMs, currentLevel)
// Handle progression - XP earned, potential level up
- val xpGained = progressionManager.calculateGameXP(finalScore, gameBoard.lines, currentLevel, timePlayedMs, statsManager.getSessionTetrises(), 0)
+ val xpGained = progressionManager.calculateGameXP(
+ score = finalScore,
+ linesCleared = gameBoard.lines,
+ level = currentLevel,
+ gameTime = timePlayedMs,
+ tSpins = statsManager.getSessionTSpins(),
+ combos = statsManager.getSessionMaxCombo(),
+ quadCount = statsManager.getSessionQuads(),
+ perfectClearCount = statsManager.getSessionPerfectClears()
+ )
val newRewards = progressionManager.addXP(xpGained)
// Show progression screen if player earned XP
diff --git a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt
index 09b9b9c..1652365 100644
--- a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt
+++ b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt
@@ -78,7 +78,7 @@ class StatsActivity : AppCompatActivity() {
binding.totalSinglesText.setTextColor(textColor)
binding.totalDoublesText.setTextColor(textColor)
binding.totalTriplesText.setTextColor(textColor)
- binding.totalTetrisesText.setTextColor(textColor)
+ binding.totalQuadsText.setTextColor(textColor)
binding.maxLevelText.setTextColor(textColor)
binding.maxScoreText.setTextColor(textColor)
binding.maxLinesText.setTextColor(textColor)
@@ -116,7 +116,7 @@ class StatsActivity : AppCompatActivity() {
binding.totalSinglesText.text = getString(R.string.singles, statsManager.getTotalSingles())
binding.totalDoublesText.text = getString(R.string.doubles, statsManager.getTotalDoubles())
binding.totalTriplesText.text = getString(R.string.triples, statsManager.getTotalTriples())
- binding.totalTetrisesText.text = getString(R.string.tetrises, statsManager.getTotalTetrises())
+ binding.totalQuadsText.text = getString(R.string.quads, statsManager.getTotalQuads())
// Update best performance stats
binding.maxLevelText.text = getString(R.string.max_level, statsManager.getMaxLevel())
diff --git a/app/src/main/java/com/pixelmintdrop/game/GameHaptics.kt b/app/src/main/java/com/pixelmintdrop/game/GameHaptics.kt
index 9f55369..7629065 100644
--- a/app/src/main/java/com/pixelmintdrop/game/GameHaptics.kt
+++ b/app/src/main/java/com/pixelmintdrop/game/GameHaptics.kt
@@ -53,7 +53,7 @@ class GameHaptics(private val context: Context) {
1 -> (50L * multiplier).toLong() // Single line: short vibration
2 -> (80L * multiplier).toLong() // Double line: slightly longer
3 -> (120L * multiplier).toLong() // Triple line: even longer
- 4 -> (200L * multiplier).toLong() // Tetris: longest vibration
+ 4 -> (200L * multiplier).toLong() // Quad: longest vibration
else -> (50L * multiplier).toLong()
}
@@ -61,7 +61,7 @@ class GameHaptics(private val context: Context) {
1 -> (80 * multiplier).toInt().coerceAtMost(255) // Single line: mild vibration
2 -> (120 * multiplier).toInt().coerceAtMost(255) // Double line: medium vibration
3 -> (180 * multiplier).toInt().coerceAtMost(255) // Triple line: strong vibration
- 4 -> 255 // Tetris: maximum vibration
+ 4 -> 255 // Quad: maximum vibration
else -> (80 * multiplier).toInt().coerceAtMost(255)
}
diff --git a/app/src/main/java/com/pixelmintdrop/game/GameView.kt b/app/src/main/java/com/pixelmintdrop/game/GameView.kt
index e7145d5..305e2f8 100644
--- a/app/src/main/java/com/pixelmintdrop/game/GameView.kt
+++ b/app/src/main/java/com/pixelmintdrop/game/GameView.kt
@@ -1262,11 +1262,11 @@ class GameView @JvmOverloads constructor(
// Create new animation
pulseAnimator = ValueAnimator.ofFloat(0f, 1f, 0f).apply {
duration = when (lineCount) {
- 4 -> 2000L // Tetris - longer duration
- 3 -> 1600L // Triples
- 2 -> 1200L // Doubles
- 1 -> 1000L // Singles
- else -> 1000L
+ 1 -> 500L // Single
+ 2 -> 1000L // Double
+ 3 -> 1500L // Triple
+ 4 -> 2000L // Quad - longer duration
+ else -> 500L
}
interpolator = LinearInterpolator()
addUpdateListener { animation ->
diff --git a/app/src/main/java/com/pixelmintdrop/game/GamepadController.kt b/app/src/main/java/com/pixelmintdrop/game/GamepadController.kt
index 3c15a92..83718b2 100644
--- a/app/src/main/java/com/pixelmintdrop/game/GamepadController.kt
+++ b/app/src/main/java/com/pixelmintdrop/game/GamepadController.kt
@@ -256,7 +256,7 @@ class GamepadController(
1 -> 100
2 -> 150
3 -> 200
- else -> 255 // For tetris (4 lines)
+ else -> 255 // For quad (4 lines)
}
vibrateGamepad(RUMBLE_LINE_CLEAR_DURATION_MS, amplitude)
}
diff --git a/app/src/main/java/com/pixelmintdrop/model/GameBoard.kt b/app/src/main/java/com/pixelmintdrop/model/GameBoard.kt
index bf20b4d..bca6233 100644
--- a/app/src/main/java/com/pixelmintdrop/model/GameBoard.kt
+++ b/app/src/main/java/com/pixelmintdrop/model/GameBoard.kt
@@ -1,6 +1,7 @@
package com.pixelmintdrop.model
import android.util.Log
+import java.util.*
/**
* Represents the game board (grid) and manages game state
@@ -43,7 +44,7 @@ class GameBoard(
// Scoring state
private var combo = 0
- private var lastClearWasTetris = false
+ private var lastClearWasQuad = false
private var lastClearWasPerfect = false
private var lastClearWasAllClear = false
private var lastPieceClearedLines = false // Track if the last piece placed cleared lines
@@ -69,6 +70,15 @@ class GameBoard(
private var pieceSpawnTime = 0L
private val spawnGracePeriod = 250L // Changed from 150ms to 250ms
+ private var lastClearWasTSpin = false
+ private var lastClearWasMiniTSpin = false
+ private var currentCombo = 0
+
+ private var rng: Random = Random(System.currentTimeMillis())
+
+ // Added for StatsManager
+ private var statsManager: StatsManager? = null
+
init {
spawnNextPiece()
spawnPiece()
@@ -541,8 +551,8 @@ class GameBoard(
}
} else 1.0
- // Calculate back-to-back Tetris bonus
- val backToBackMultiplier = if (clearedLines == 4 && lastClearWasTetris) 1.5 else 1.0
+ // Calculate back-to-back Quad bonus
+ val backToBackMultiplier = if ((clearedLines == 4 && lastClearWasQuad) || (isTSpin() && lastClearWasTSpin)) 1.5 else 1.0
// Calculate perfect clear bonus
val perfectClearMultiplier = if (isPerfectClear) {
@@ -579,7 +589,7 @@ class GameBoard(
}.start()
// Update line clear state
- lastClearWasTetris = clearedLines == 4
+ lastClearWasQuad = clearedLines == 4
lastClearWasPerfect = isPerfectClear
lastClearWasAllClear = isAllClear
@@ -702,7 +712,7 @@ class GameBoard(
// Reset scoring state
combo = 0
- lastClearWasTetris = false
+ lastClearWasQuad = false
lastClearWasPerfect = false
lastClearWasAllClear = false
lastPieceClearedLines = false
@@ -762,4 +772,11 @@ class GameBoard(
/**
* Update the game level
*/
+
+ /**
+ * Set the StatsManager instance for recording stats
+ */
+ fun setStatsManager(manager: StatsManager) {
+ statsManager = manager
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt b/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt
index 3e0a568..457afa0 100644
--- a/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt
+++ b/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt
@@ -93,24 +93,26 @@ class PlayerProgressionManager(context: Context) {
/**
* Calculate XP from a game session based on score, lines, level, etc.
*/
- fun calculateGameXP(score: Int, lines: Int, level: Int, gameTime: Long,
- tetrisCount: Int, perfectClearCount: Int): Long {
+ fun calculateGameXP(score: Int, linesCleared: Int, level: Int, gameTime: Long,
+ tSpins: Int, combos: Int, quadCount: Int, perfectClearCount: Int): Long {
// Base XP from score with level multiplier (capped at level 10)
val cappedLevel = min(level, 10)
val scoreXP = (score * (1 + LEVEL_MULTIPLIER * cappedLevel)).toLong()
// XP from lines cleared (reduced for higher levels)
- val linesXP = lines * XP_PER_LINE * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
+ val linesXP = linesCleared * XP_PER_LINE * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
// XP from special moves (reduced for higher levels)
- val tetrisBonus = tetrisCount * TETRIS_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
- val perfectClearBonus = perfectClearCount * PERFECT_CLEAR_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
+ val tSpinXP = tSpins * T_SPIN_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
+ val combosXP = combos * COMBO_XP_BONUS
+ val quadBonus = quadCount * QUAD_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
+ val perfectClearBonus = perfectClearCount * PERFECT_CLEAR_XP_BONUS
// Time bonus (reduced for longer games)
val timeBonus = (gameTime / 60000) * TIME_XP_PER_MINUTE * (1 - (gameTime / 3600000) * 0.1).coerceAtLeast(0.5)
// Calculate total XP
- return (scoreXP + linesXP + tetrisBonus + perfectClearBonus + timeBonus).toLong()
+ return (scoreXP + linesXP + tSpinXP + combosXP + quadBonus + perfectClearBonus + timeBonus).toLong()
}
/**
@@ -306,8 +308,10 @@ class PlayerProgressionManager(context: Context) {
private const val XP_CURVE_FACTOR = 2.0
private const val LEVEL_MULTIPLIER = 0.03
private const val XP_PER_LINE = 40L
- private const val TETRIS_XP_BONUS = 150L
- private const val PERFECT_CLEAR_XP_BONUS = 300L
+ private const val COMBO_XP_BONUS = 5L
+ private const val PERFECT_CLEAR_XP_BONUS = 200L
+ private const val T_SPIN_XP_BONUS = 80L
+ private const val QUAD_XP_BONUS = 150L
private const val TIME_XP_PER_MINUTE = 20L
// Theme constants
diff --git a/app/src/main/java/com/pixelmintdrop/model/StatsManager.kt b/app/src/main/java/com/pixelmintdrop/model/StatsManager.kt
index 89de6a0..5da4e4e 100644
--- a/app/src/main/java/com/pixelmintdrop/model/StatsManager.kt
+++ b/app/src/main/java/com/pixelmintdrop/model/StatsManager.kt
@@ -16,11 +16,15 @@ class StatsManager(context: Context) {
private var maxScore: Int = 0
private var maxLines: Int = 0
+ // Special move stats (lifetime)
+ private var totalTSpins: Int = 0
+ private var totalPerfectClears: Int = 0
+
// Line clear stats (lifetime)
private var totalSingles: Int = 0
private var totalDoubles: Int = 0
private var totalTriples: Int = 0
- private var totalTetrises: Int = 0
+ private var totalQuads: Int = 0
// Session stats
private var sessionScore: Int = 0
@@ -29,11 +33,16 @@ class StatsManager(context: Context) {
private var sessionTime: Long = 0
private var sessionLevel: Int = 0
+ // Special move stats (session)
+ private var sessionTSpins: Int = 0
+ private var sessionPerfectClears: Int = 0
+ private var sessionMaxCombo: Int = 0
+
// Line clear stats (session)
private var sessionSingles: Int = 0
private var sessionDoubles: Int = 0
private var sessionTriples: Int = 0
- private var sessionTetrises: Int = 0
+ private var sessionQuads: Int = 0
init {
loadStats()
@@ -49,11 +58,15 @@ class StatsManager(context: Context) {
maxScore = prefs.getInt(KEY_MAX_SCORE, 0)
maxLines = prefs.getInt(KEY_MAX_LINES, 0)
+ // Load special move stats
+ totalTSpins = prefs.getInt(KEY_TOTAL_T_SPINS, 0)
+ totalPerfectClears = prefs.getInt(KEY_TOTAL_PERFECT_CLEARS, 0)
+
// Load line clear stats
totalSingles = prefs.getInt(KEY_TOTAL_SINGLES, 0)
totalDoubles = prefs.getInt(KEY_TOTAL_DOUBLES, 0)
totalTriples = prefs.getInt(KEY_TOTAL_TRIPLES, 0)
- totalTetrises = prefs.getInt(KEY_TOTAL_TETRISES, 0)
+ totalQuads = prefs.getInt(KEY_TOTAL_QUADS, 0)
}
private fun saveStats() {
@@ -67,9 +80,11 @@ class StatsManager(context: Context) {
.putInt(KEY_MAX_SCORE, maxScore)
.putInt(KEY_MAX_LINES, maxLines)
.putInt(KEY_TOTAL_SINGLES, totalSingles)
+ .putInt(KEY_TOTAL_T_SPINS, totalTSpins)
+ .putInt(KEY_TOTAL_PERFECT_CLEARS, totalPerfectClears)
.putInt(KEY_TOTAL_DOUBLES, totalDoubles)
.putInt(KEY_TOTAL_TRIPLES, totalTriples)
- .putInt(KEY_TOTAL_TETRISES, totalTetrises)
+ .putInt(KEY_TOTAL_QUADS, totalQuads)
.apply()
}
@@ -82,7 +97,10 @@ class StatsManager(context: Context) {
sessionSingles = 0
sessionDoubles = 0
sessionTriples = 0
- sessionTetrises = 0
+ sessionQuads = 0
+ sessionTSpins = 0
+ sessionPerfectClears = 0
+ sessionMaxCombo = 0
}
fun updateSessionStats(score: Int, lines: Int, pieces: Int, time: Long, level: Int) {
@@ -108,8 +126,8 @@ class StatsManager(context: Context) {
totalTriples++
}
4 -> {
- sessionTetrises++
- totalTetrises++
+ sessionQuads++
+ totalQuads++
}
}
}
@@ -142,7 +160,11 @@ class StatsManager(context: Context) {
fun getTotalSingles(): Int = totalSingles
fun getTotalDoubles(): Int = totalDoubles
fun getTotalTriples(): Int = totalTriples
- fun getTotalTetrises(): Int = totalTetrises
+ fun getTotalQuads(): Int = totalQuads
+
+ // Getters for special move stats (lifetime)
+ fun getTotalTSpins(): Int = totalTSpins
+ fun getTotalPerfectClears(): Int = totalPerfectClears
// Getters for session stats
fun getSessionScore(): Int = sessionScore
@@ -150,12 +172,17 @@ class StatsManager(context: Context) {
fun getSessionPieces(): Int = sessionPieces
fun getSessionTime(): Long = sessionTime
fun getSessionLevel(): Int = sessionLevel
+ fun getSessionTriples(): Int = sessionTriples
+ fun getSessionQuads(): Int = sessionQuads
- // Getters for line clear stats (session)
+ // Add missing getters for session line clears
fun getSessionSingles(): Int = sessionSingles
fun getSessionDoubles(): Int = sessionDoubles
- fun getSessionTriples(): Int = sessionTriples
- fun getSessionTetrises(): Int = sessionTetrises
+
+ // Getters for special move stats (session)
+ fun getSessionTSpins(): Int = sessionTSpins
+ fun getSessionPerfectClears(): Int = sessionPerfectClears
+ fun getSessionMaxCombo(): Int = sessionMaxCombo
fun resetStats() {
// Reset all lifetime stats
@@ -172,7 +199,11 @@ class StatsManager(context: Context) {
totalSingles = 0
totalDoubles = 0
totalTriples = 0
- totalTetrises = 0
+ totalQuads = 0
+
+ // Reset special move stats
+ totalTSpins = 0
+ totalPerfectClears = 0
// Save the reset stats
saveStats()
@@ -189,8 +220,10 @@ class StatsManager(context: Context) {
private const val KEY_MAX_SCORE = "max_score"
private const val KEY_MAX_LINES = "max_lines"
private const val KEY_TOTAL_SINGLES = "total_singles"
+ private const val KEY_TOTAL_T_SPINS = "total_t_spins"
+ private const val KEY_TOTAL_PERFECT_CLEARS = "total_perfect_clears"
private const val KEY_TOTAL_DOUBLES = "total_doubles"
private const val KEY_TOTAL_TRIPLES = "total_triples"
- private const val KEY_TOTAL_TETRISES = "total_tetrises"
+ private const val KEY_TOTAL_QUADS = "total_quads"
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml
index 582437e..eb8a729 100644
--- a/app/src/main/res/layout-land/activity_main.xml
+++ b/app/src/main/res/layout-land/activity_main.xml
@@ -592,7 +592,7 @@
android:fontFamily="sans-serif" />
+ android:fontFamily="sans-serif"
+ android:text="T-Spins: 0" />
singles: %d
doubles: %d
triples: %d
- tetrises: %d
+ quads: %d
reset stats
music
Customization
Random Mode
+ Next
+ Hold
+ lines: %d
+ t-spins: %d
+ perfect clears: %d
+ max combo: %d
\ No newline at end of file