diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aaa37f3..bc5761a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,12 +28,7 @@ - - + android:theme="@style/Theme.AppCompat.NoActionBar" + android:exported="false" /> \ No newline at end of file diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index fd49615..d6eac7e 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -21,9 +21,6 @@ import com.mintris.model.GameBoard import com.mintris.audio.GameMusic import com.mintris.model.HighScoreManager import android.content.Intent -import com.mintris.model.StatsManager -import java.text.SimpleDateFormat -import java.util.* class MainActivity : AppCompatActivity() { @@ -35,7 +32,6 @@ class MainActivity : AppCompatActivity() { private lateinit var gameMusic: GameMusic private lateinit var titleScreen: TitleScreen private lateinit var highScoreManager: HighScoreManager - private lateinit var statsManager: StatsManager // Game state private var isSoundEnabled = true @@ -44,8 +40,6 @@ class MainActivity : AppCompatActivity() { private val maxLevel = 20 private var currentScore = 0 private var currentLevel = 1 - private var gameStartTime: Long = 0 - private var piecesPlaced: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -59,7 +53,6 @@ class MainActivity : AppCompatActivity() { titleScreen = binding.titleScreen gameMusic = GameMusic(this) highScoreManager = HighScoreManager(this) - statsManager = StatsManager(this) // Set up game view gameView.setGameBoard(gameBoard) @@ -124,8 +117,6 @@ class MainActivity : AppCompatActivity() { } else { android.util.Log.d("MainActivity", "Sound is disabled, skipping haptic feedback") } - // Record line clear in stats - statsManager.recordLineClear(lineCount) } // Add callbacks for piece movement and locking @@ -139,7 +130,6 @@ class MainActivity : AppCompatActivity() { if (isSoundEnabled) { gameHaptics.vibrateForPieceLock() } - piecesPlaced++ } // Set up button click listeners with haptic feedback @@ -197,13 +187,6 @@ class MainActivity : AppCompatActivity() { } } - // Set up stats button - binding.statsButton.setOnClickListener { - gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY) - val intent = Intent(this, StatsActivity::class.java) - startActivity(intent) - } - // Initialize level selector updateLevelSelector() @@ -222,9 +205,6 @@ class MainActivity : AppCompatActivity() { binding.linesText.text = lines.toString() binding.comboText.text = gameBoard.getCombo().toString() - // Update current level for stats - currentLevel = level - // Force redraw of next piece preview binding.nextPieceView.invalidate() } @@ -233,35 +213,7 @@ class MainActivity : AppCompatActivity() { * Show game over screen */ private fun showGameOver(score: Int) { - val gameTime = System.currentTimeMillis() - gameStartTime - - // Update session stats - statsManager.updateSessionStats( - score = score, - lines = gameBoard.lines, - pieces = piecesPlaced, - time = gameTime, - level = currentLevel - ) - - // End session and save stats - statsManager.endSession() - - // Update session stats display - val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) - timeFormat.timeZone = TimeZone.getTimeZone("UTC") - - binding.sessionScoreText.text = getString(R.string.session_score, score) - binding.sessionLinesText.text = getString(R.string.session_lines, gameBoard.lines) - binding.sessionPiecesText.text = getString(R.string.session_pieces, piecesPlaced) - binding.sessionTimeText.text = getString(R.string.session_time, timeFormat.format(gameTime)) - binding.sessionLevelText.text = getString(R.string.session_level, currentLevel) - - // Update session line clear stats - 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.finalScoreText.text = getString(R.string.score) + ": " + score // Check if this is a high score if (highScoreManager.isHighScore(score)) { @@ -339,13 +291,10 @@ class MainActivity : AppCompatActivity() { private fun startGame() { gameView.start() - gameMusic.setEnabled(isMusicEnabled) + gameMusic.setEnabled(isMusicEnabled) // Explicitly set enabled state if (isMusicEnabled) { gameMusic.start() } - gameStartTime = System.currentTimeMillis() - piecesPlaced = 0 - statsManager.startNewSession() } private fun restartGame() { diff --git a/app/src/main/java/com/mintris/StatsActivity.kt b/app/src/main/java/com/mintris/StatsActivity.kt deleted file mode 100644 index 269adb0..0000000 --- a/app/src/main/java/com/mintris/StatsActivity.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.mintris - -import android.os.Bundle -import android.widget.Button -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import com.mintris.databinding.ActivityStatsBinding -import com.mintris.model.StatsManager -import java.text.SimpleDateFormat -import java.util.* - -class StatsActivity : AppCompatActivity() { - private lateinit var binding: ActivityStatsBinding - private lateinit var statsManager: StatsManager - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityStatsBinding.inflate(layoutInflater) - setContentView(binding.root) - - statsManager = StatsManager(this) - - // Set up back button - binding.backButton.setOnClickListener { - finish() - } - - updateStats() - } - - private fun updateStats() { - // Format time duration - val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) - timeFormat.timeZone = TimeZone.getTimeZone("UTC") - - // Update lifetime stats - binding.totalGamesText.text = getString(R.string.total_games, statsManager.getTotalGames()) - binding.totalScoreText.text = getString(R.string.total_score, statsManager.getTotalScore()) - binding.totalLinesText.text = getString(R.string.total_lines, statsManager.getTotalLines()) - binding.totalPiecesText.text = getString(R.string.total_pieces, statsManager.getTotalPieces()) - binding.totalTimeText.text = getString(R.string.total_time, timeFormat.format(statsManager.getTotalTime())) - - // Update line clear stats - 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()) - - // Update best performance stats - binding.maxLevelText.text = getString(R.string.max_level, statsManager.getMaxLevel()) - binding.maxScoreText.text = getString(R.string.max_score, statsManager.getMaxScore()) - binding.maxLinesText.text = getString(R.string.max_lines, statsManager.getMaxLines()) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/mintris/game/TitleScreen.kt b/app/src/main/java/com/mintris/game/TitleScreen.kt index 3a8978d..379caaf 100644 --- a/app/src/main/java/com/mintris/game/TitleScreen.kt +++ b/app/src/main/java/com/mintris/game/TitleScreen.kt @@ -12,10 +12,6 @@ import java.util.Random import android.util.Log import com.mintris.model.HighScoreManager import com.mintris.model.HighScore -import kotlin.math.abs -import androidx.core.graphics.withTranslation -import androidx.core.graphics.withScale -import androidx.core.graphics.withRotation class TitleScreen @JvmOverloads constructor( context: Context, @@ -33,14 +29,6 @@ class TitleScreen @JvmOverloads constructor( private var width = 0 private var height = 0 private val tetrominosToAdd = mutableListOf() - private val highScoreManager = HighScoreManager(context) // Pre-allocate HighScoreManager - - // Touch handling variables - private var startX = 0f - private var startY = 0f - private var lastTouchX = 0f - private var lastTouchY = 0f - private val maxTapMovement = 20f // Maximum movement allowed for a tap (in pixels) // Callback for when the user touches the screen var onStartGame: (() -> Unit)? = null @@ -199,31 +187,58 @@ class TitleScreen @JvmOverloads constructor( tetrominosToAdd.add(createRandomTetromino()) } else { try { + // Save canvas state before rotation + canvas.save() + + // Translate to the tetromino's position + canvas.translate(tetromino.x, tetromino.y) + + // Scale according to the tetromino's scale factor + canvas.scale(tetromino.scale, tetromino.scale) + + // Rotate around the center of the tetromino + val centerX = tetromino.shape.size * cellSize / 2 + val centerY = tetromino.shape.size * cellSize / 2 + canvas.rotate(tetromino.rotation.toFloat(), centerX, centerY) + // Draw the tetromino - for (y in 0 until tetromino.shape.size) { - for (x in 0 until tetromino.shape.size) { - if (tetromino.shape[y][x] == 1) { - val left = x * cellSize - val top = y * cellSize - val right = left + cellSize - val bottom = top + cellSize + for (row in tetromino.shape.indices) { + for (col in 0 until tetromino.shape[row].size) { + if (tetromino.shape[row][col] == 1) { + // Draw larger glow effect + glowPaint.alpha = 30 + canvas.drawRect( + col * cellSize - 8, + row * cellSize - 8, + (col + 1) * cellSize + 8, + (row + 1) * cellSize + 8, + glowPaint + ) - // Draw block with glow effect - canvas.withTranslation(tetromino.x, tetromino.y) { - withScale(tetromino.scale, tetromino.scale) { - withRotation(tetromino.rotation.toFloat(), - tetromino.shape.size * cellSize / 2, - tetromino.shape.size * cellSize / 2) { - // Draw glow - canvas.drawRect(left - 8f, top - 8f, right + 8f, bottom + 8f, glowPaint) - // Draw block - canvas.drawRect(left, top, right, bottom, paint) - } - } - } + // Draw medium glow + glowPaint.alpha = 60 + canvas.drawRect( + col * cellSize - 4, + row * cellSize - 4, + (col + 1) * cellSize + 4, + (row + 1) * cellSize + 4, + glowPaint + ) + + // Draw main block + canvas.drawRect( + col * cellSize, + row * cellSize, + (col + 1) * cellSize, + (row + 1) * cellSize, + paint + ) } } } + + // Restore canvas state + canvas.restore() } catch (e: Exception) { Log.e("TitleScreen", "Error drawing tetromino", e) } @@ -237,7 +252,8 @@ class TitleScreen @JvmOverloads constructor( val titleY = height * 0.4f canvas.drawText("mintris", width / 2f, titleY, titlePaint) - // Draw high scores using pre-allocated manager + // Draw high scores + val highScoreManager = HighScoreManager(context) val highScores: List = highScoreManager.getHighScores() val highScoreY = height * 0.5f if (highScores.isNotEmpty()) { @@ -258,50 +274,10 @@ class TitleScreen @JvmOverloads constructor( } override fun onTouchEvent(event: MotionEvent): Boolean { - when (event.action) { - MotionEvent.ACTION_DOWN -> { - startX = event.x - startY = event.y - lastTouchX = event.x - lastTouchY = event.y - return true - } - MotionEvent.ACTION_MOVE -> { - val deltaX = event.x - lastTouchX - val deltaY = event.y - lastTouchY - - // Update tetromino positions - for (tetromino in tetrominos) { - tetromino.x += deltaX * 0.5f - tetromino.y += deltaY * 0.5f - } - - lastTouchX = event.x - lastTouchY = event.y - invalidate() - return true - } - MotionEvent.ACTION_UP -> { - val deltaX = event.x - startX - val deltaY = event.y - startY - - // If the movement was minimal, treat as a tap - if (abs(deltaX) < maxTapMovement && abs(deltaY) < maxTapMovement) { - performClick() - } - - return true - } + if (event.action == MotionEvent.ACTION_DOWN) { + onStartGame?.invoke() + return true } return super.onTouchEvent(event) } - - override fun performClick(): Boolean { - // Call the superclass's performClick - super.performClick() - - // Handle the click event - onStartGame?.invoke() - return true - } } \ No newline at end of file diff --git a/app/src/main/java/com/mintris/model/GameBoard.kt b/app/src/main/java/com/mintris/model/GameBoard.kt index 42deaf3..3119971 100644 --- a/app/src/main/java/com/mintris/model/GameBoard.kt +++ b/app/src/main/java/com/mintris/model/GameBoard.kt @@ -31,8 +31,6 @@ class GameBoard( var level = 1 var lines = 0 var isGameOver = false - var isHardDropInProgress = false // Make public - var isPieceLocking = false // Make public // Scoring state private var combo = 0 @@ -152,9 +150,6 @@ class GameBoard( * Move the current piece down (soft drop) */ fun moveDown(): Boolean { - // Don't allow movement if a hard drop is in progress or piece is locking - if (isHardDropInProgress || isPieceLocking) return false - return if (canMove(0, 1)) { currentPiece?.y = currentPiece?.y?.plus(1) ?: 0 onPieceMove?.invoke() @@ -169,19 +164,9 @@ class GameBoard( * Hard drop the current piece */ fun hardDrop() { - if (isHardDropInProgress || isPieceLocking) return // Prevent multiple hard drops - - isHardDropInProgress = true - val piece = currentPiece ?: return - - // Move piece down until it can't move anymore - while (canMove(0, 1)) { - piece.y++ - onPieceMove?.invoke() + while (moveDown()) { + // Keep moving down until blocked } - - // Lock the piece immediately - lockPiece() } /** @@ -264,12 +249,9 @@ class GameBoard( } /** - * Lock the current piece in place + * Lock the current piece in place and check for completed lines */ - private fun lockPiece() { - if (isPieceLocking) return // Prevent recursive locking - isPieceLocking = true - + fun lockPiece() { val piece = currentPiece ?: return // Add the piece to the grid @@ -293,15 +275,11 @@ class GameBoard( // Find and clear lines immediately findAndClearLines() - // Spawn new piece immediately + // Spawn new piece spawnPiece() // Allow holding piece again after locking canHold = true - - // Reset both states after everything is done - isPieceLocking = false - isHardDropInProgress = false } /** diff --git a/app/src/main/java/com/mintris/model/StatsManager.kt b/app/src/main/java/com/mintris/model/StatsManager.kt deleted file mode 100644 index 3a3eae7..0000000 --- a/app/src/main/java/com/mintris/model/StatsManager.kt +++ /dev/null @@ -1,175 +0,0 @@ -package com.mintris.model - -import android.content.Context -import android.content.SharedPreferences - -class StatsManager(context: Context) { - private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) - - // Lifetime stats - private var totalGames: Int = 0 - private var totalScore: Long = 0 - private var totalLines: Int = 0 - private var totalPieces: Int = 0 - private var totalTime: Long = 0 - private var maxLevel: Int = 0 - private var maxScore: Int = 0 - private var maxLines: 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 - - // Session stats - private var sessionScore: Int = 0 - private var sessionLines: Int = 0 - private var sessionPieces: Int = 0 - private var sessionTime: Long = 0 - private var sessionLevel: 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 - - init { - loadStats() - } - - private fun loadStats() { - totalGames = prefs.getInt(KEY_TOTAL_GAMES, 0) - totalScore = prefs.getLong(KEY_TOTAL_SCORE, 0) - totalLines = prefs.getInt(KEY_TOTAL_LINES, 0) - totalPieces = prefs.getInt(KEY_TOTAL_PIECES, 0) - totalTime = prefs.getLong(KEY_TOTAL_TIME, 0) - maxLevel = prefs.getInt(KEY_MAX_LEVEL, 0) - maxScore = prefs.getInt(KEY_MAX_SCORE, 0) - maxLines = prefs.getInt(KEY_MAX_LINES, 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) - } - - private fun saveStats() { - prefs.edit() - .putInt(KEY_TOTAL_GAMES, totalGames) - .putLong(KEY_TOTAL_SCORE, totalScore) - .putInt(KEY_TOTAL_LINES, totalLines) - .putInt(KEY_TOTAL_PIECES, totalPieces) - .putLong(KEY_TOTAL_TIME, totalTime) - .putInt(KEY_MAX_LEVEL, maxLevel) - .putInt(KEY_MAX_SCORE, maxScore) - .putInt(KEY_MAX_LINES, maxLines) - .putInt(KEY_TOTAL_SINGLES, totalSingles) - .putInt(KEY_TOTAL_DOUBLES, totalDoubles) - .putInt(KEY_TOTAL_TRIPLES, totalTriples) - .putInt(KEY_TOTAL_TETRISES, totalTetrises) - .apply() - } - - fun startNewSession() { - sessionScore = 0 - sessionLines = 0 - sessionPieces = 0 - sessionTime = 0 - sessionLevel = 0 - sessionSingles = 0 - sessionDoubles = 0 - sessionTriples = 0 - sessionTetrises = 0 - } - - fun updateSessionStats(score: Int, lines: Int, pieces: Int, time: Long, level: Int) { - sessionScore = score - sessionLines = lines - sessionPieces = pieces - sessionTime = time - sessionLevel = level - } - - fun recordLineClear(lineCount: Int) { - when (lineCount) { - 1 -> { - sessionSingles++ - totalSingles++ - } - 2 -> { - sessionDoubles++ - totalDoubles++ - } - 3 -> { - sessionTriples++ - totalTriples++ - } - 4 -> { - sessionTetrises++ - totalTetrises++ - } - } - } - - fun endSession() { - totalGames++ - totalScore += sessionScore - totalLines += sessionLines - totalPieces += sessionPieces - totalTime += sessionTime - - if (sessionLevel > maxLevel) maxLevel = sessionLevel - if (sessionScore > maxScore) maxScore = sessionScore - if (sessionLines > maxLines) maxLines = sessionLines - - saveStats() - } - - // Getters for lifetime stats - fun getTotalGames(): Int = totalGames - fun getTotalScore(): Long = totalScore - fun getTotalLines(): Int = totalLines - fun getTotalPieces(): Int = totalPieces - fun getTotalTime(): Long = totalTime - fun getMaxLevel(): Int = maxLevel - fun getMaxScore(): Int = maxScore - fun getMaxLines(): Int = maxLines - - // Getters for line clear stats (lifetime) - fun getTotalSingles(): Int = totalSingles - fun getTotalDoubles(): Int = totalDoubles - fun getTotalTriples(): Int = totalTriples - fun getTotalTetrises(): Int = totalTetrises - - // Getters for session stats - fun getSessionScore(): Int = sessionScore - fun getSessionLines(): Int = sessionLines - fun getSessionPieces(): Int = sessionPieces - fun getSessionTime(): Long = sessionTime - fun getSessionLevel(): Int = sessionLevel - - // Getters for line clear stats (session) - fun getSessionSingles(): Int = sessionSingles - fun getSessionDoubles(): Int = sessionDoubles - fun getSessionTriples(): Int = sessionTriples - fun getSessionTetrises(): Int = sessionTetrises - - companion object { - private const val PREFS_NAME = "mintris_stats" - private const val KEY_TOTAL_GAMES = "total_games" - private const val KEY_TOTAL_SCORE = "total_score" - private const val KEY_TOTAL_LINES = "total_lines" - private const val KEY_TOTAL_PIECES = "total_pieces" - private const val KEY_TOTAL_TIME = "total_time" - private const val KEY_MAX_LEVEL = "max_level" - 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_DOUBLES = "total_doubles" - private const val KEY_TOTAL_TRIPLES = "total_triples" - private const val KEY_TOTAL_TETRISES = "total_tetrises" - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ab63a51..7271d89 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -135,92 +135,10 @@ android:textStyle="bold" /> - - - - - - - - - - - - - - - - - - - - @@ -291,16 +209,6 @@ android:text="@string/high_scores" android:textColor="@color/white" android:textSize="18sp" /> - -