From 2774703df5b9227851a8e52e8e9ab7b646f8a6dc Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 19:36:14 -0400 Subject: [PATCH 01/11] fix: persist block skin selection through app restarts --- app/src/main/java/com/mintris/MainActivity.kt | 7 ++++++ .../main/java/com/mintris/game/GameView.kt | 7 ++++++ .../mintris/model/PlayerProgressionManager.kt | 24 +++++++++---------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index 5390803..c084f6c 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -98,6 +98,13 @@ class MainActivity : AppCompatActivity() { // Load and apply block skin preference gameView.setBlockSkin(progressionManager.getSelectedBlockSkin()) + // Update block skin selector with current selection + blockSkinSelector.updateBlockSkins( + progressionManager.getUnlockedBlocks(), + gameView.getCurrentBlockSkin(), + progressionManager.getPlayerLevel() + ) + // Set up game view gameView.setGameBoard(gameBoard) gameView.setHaptics(gameHaptics) diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index fe388b5..e296d14 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -173,6 +173,10 @@ class GameView @JvmOverloads constructor( // Start with paused state pause() + // Load saved block skin + val prefs = context.getSharedPreferences("mintris_progression", Context.MODE_PRIVATE) + currentBlockSkin = prefs.getString("selected_block_skin", "block_skin_1") ?: "block_skin_1" + // Connect our callbacks to the GameBoard gameBoard.onPieceMove = { onPieceMove?.invoke() } gameBoard.onPieceLock = { @@ -269,6 +273,9 @@ class GameView @JvmOverloads constructor( */ fun setBlockSkin(skinId: String) { currentBlockSkin = skinId + // Save the selection to SharedPreferences + val prefs = context.getSharedPreferences("mintris_progression", Context.MODE_PRIVATE) + prefs.edit().putString("selected_block_skin", skinId).commit() invalidate() } diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index bf62a6a..7f95acc 100644 --- a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt @@ -281,21 +281,19 @@ class PlayerProgressionManager(context: Context) { private const val KEY_UNLOCKED_THEMES = "unlocked_themes" private const val KEY_UNLOCKED_BLOCKS = "unlocked_blocks" private const val KEY_UNLOCKED_BADGES = "unlocked_badges" - private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin" private const val KEY_SELECTED_THEME = "selected_theme" + private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin" - // XP curve parameters - private const val BASE_XP = 4000.0 // Base XP for level 1 (reduced from 5000) - private const val XP_CURVE_FACTOR = 1.9 // Exponential factor for XP curve (reduced from 2.2) + // XP constants + private const val BASE_XP = 1000L + private const val XP_CURVE_FACTOR = 1.5 + private const val LEVEL_MULTIPLIER = 0.1 + private const val XP_PER_LINE = 100L + private const val TETRIS_XP_BONUS = 500L + private const val PERFECT_CLEAR_XP_BONUS = 1000L + private const val TIME_XP_PER_MINUTE = 50L - // XP calculation constants - private const val LEVEL_MULTIPLIER = 0.15 // 15% bonus per level (increased from 10%) - private const val XP_PER_LINE = 15L // Increased from 10 - private const val TETRIS_XP_BONUS = 75L // Increased from 50 - private const val PERFECT_CLEAR_XP_BONUS = 250L // Increased from 200 - private const val TIME_XP_PER_MINUTE = 8L // Increased from 5 - - // Theme IDs with required levels + // Theme constants const val THEME_CLASSIC = "theme_classic" const val THEME_NEON = "theme_neon" const val THEME_MONOCHROME = "theme_monochrome" @@ -326,7 +324,7 @@ class PlayerProgressionManager(context: Context) { */ fun setSelectedBlockSkin(skinId: String) { if (unlockedBlocks.contains(skinId)) { - prefs.edit().putString(KEY_SELECTED_BLOCK_SKIN, skinId).apply() + prefs.edit().putString(KEY_SELECTED_BLOCK_SKIN, skinId).commit() } } From 7cdc9988cb2042cddaf85e5d1e7f359663454157 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:17:44 -0400 Subject: [PATCH 02/11] fix: enhance block skins to match their respective themes - Classic: Clean white blocks with subtle glow - Neon: Strong glow effects with magenta colors - Retro: Pixelated look with highlights and shadows - Minimalist: Clean black blocks with subtle borders - Galaxy: Cosmic effects with gradient and sparkles --- .../main/java/com/mintris/game/GameView.kt | 194 +++++++++++++----- 1 file changed, 137 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index e296d14..e4d7625 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -2,12 +2,14 @@ package com.mintris.game import android.animation.ValueAnimator import android.content.Context +import android.graphics.BlurMaskFilter import android.graphics.Canvas import android.graphics.Color +import android.graphics.LinearGradient import android.graphics.Paint import android.graphics.Rect import android.graphics.RectF -import android.graphics.BlurMaskFilter +import android.graphics.Shader import android.os.Build import android.os.Handler import android.os.Looper @@ -236,6 +238,7 @@ class GameView @JvmOverloads constructor( blockSkinPaints["block_skin_1"] = Paint().apply { color = Color.WHITE isAntiAlias = true + style = Paint.Style.FILL } // Neon skin @@ -248,9 +251,8 @@ class GameView @JvmOverloads constructor( // Retro skin blockSkinPaints["block_skin_3"] = Paint().apply { color = Color.parseColor("#FF5A5F") - isAntiAlias = true - style = Paint.Style.STROKE - strokeWidth = 2f + isAntiAlias = false // Pixelated look + style = Paint.Style.FILL } // Minimalist skin @@ -601,61 +603,139 @@ class GameView @JvmOverloads constructor( // Create a clone of the paint to avoid modifying the original val blockPaint = Paint(paint) - // Special handling for neon skin - if (currentBlockSkin == "block_skin_2") { - // Stronger outer glow for neon skin - blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 0, 255) else Color.parseColor("#FF00FF") - blockGlowPaint.maskFilter = BlurMaskFilter(16f, BlurMaskFilter.Blur.OUTER) - canvas.drawRect(left - 4f, top - 4f, right + 4f, bottom + 4f, blockGlowPaint) - - // For neon, use semi-translucent fill with strong glowing edges - blockPaint.style = Paint.Style.FILL_AND_STROKE - blockPaint.strokeWidth = 2f - blockPaint.maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL) - - if (isGhost) { - blockPaint.color = Color.argb(30, 255, 0, 255) - blockPaint.alpha = 30 - } else { - blockPaint.color = Color.parseColor("#66004D") // Darker magenta fill - blockPaint.alpha = 170 // More opaque to be more visible + // Draw block based on current skin + when (currentBlockSkin) { + "block_skin_1" -> { // Classic + // Draw outer glow + blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE + canvas.drawRect(left - 2f, top - 2f, right + 2f, bottom + 2f, blockGlowPaint) + + // Draw block + blockPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE + blockPaint.alpha = if (isGhost) 30 else 255 + canvas.drawRect(left, top, right, bottom, blockPaint) + + // Draw inner glow + glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE + canvas.drawRect(left + 1f, top + 1f, right - 1f, bottom - 1f, glowPaint) } - - // Draw block with neon effect - canvas.drawRect(left, top, right, bottom, blockPaint) - - // Draw a brighter border for better visibility - val borderPaint = Paint().apply { - color = Color.parseColor("#FF00FF") - style = Paint.Style.STROKE - strokeWidth = 3f - alpha = 255 - isAntiAlias = true - maskFilter = BlurMaskFilter(6f, BlurMaskFilter.Blur.NORMAL) + "block_skin_2" -> { // Neon + // Stronger outer glow for neon skin + blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 0, 255) else Color.parseColor("#FF00FF") + blockGlowPaint.maskFilter = BlurMaskFilter(16f, BlurMaskFilter.Blur.OUTER) + canvas.drawRect(left - 4f, top - 4f, right + 4f, bottom + 4f, blockGlowPaint) + + // For neon, use semi-translucent fill with strong glowing edges + blockPaint.style = Paint.Style.FILL_AND_STROKE + blockPaint.strokeWidth = 2f + blockPaint.maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL) + + if (isGhost) { + blockPaint.color = Color.argb(30, 255, 0, 255) + blockPaint.alpha = 30 + } else { + blockPaint.color = Color.parseColor("#66004D") // Darker magenta fill + blockPaint.alpha = 170 // More opaque to be more visible + } + + // Draw block with neon effect + canvas.drawRect(left, top, right, bottom, blockPaint) + + // Draw a brighter border for better visibility + val borderPaint = Paint().apply { + color = Color.parseColor("#FF00FF") + style = Paint.Style.STROKE + strokeWidth = 3f + alpha = 255 + isAntiAlias = true + maskFilter = BlurMaskFilter(6f, BlurMaskFilter.Blur.NORMAL) + } + canvas.drawRect(left, top, right, bottom, borderPaint) + + // Inner glow for neon blocks + glowPaint.color = if (isGhost) Color.argb(10, 255, 0, 255) else Color.parseColor("#FF00FF") + glowPaint.alpha = if (isGhost) 10 else 100 + glowPaint.style = Paint.Style.STROKE + glowPaint.strokeWidth = 2f + glowPaint.maskFilter = BlurMaskFilter(4f, BlurMaskFilter.Blur.NORMAL) + canvas.drawRect(left + 4f, top + 4f, right - 4f, bottom - 4f, glowPaint) + } + "block_skin_3" -> { // Retro + // Draw pixelated block with retro effect + blockPaint.color = if (isGhost) Color.argb(30, 255, 90, 95) else Color.parseColor("#FF5A5F") + blockPaint.alpha = if (isGhost) 30 else 255 + + // Draw main block + canvas.drawRect(left, top, right, bottom, blockPaint) + + // Draw pixelated highlights + val highlightPaint = Paint().apply { + color = Color.parseColor("#FF8A8F") + isAntiAlias = false + style = Paint.Style.FILL + } + // Top and left highlights + canvas.drawRect(left, top, right - 2f, top + 2f, highlightPaint) + canvas.drawRect(left, top, left + 2f, bottom - 2f, highlightPaint) + + // Draw pixelated shadows + val shadowPaint = Paint().apply { + color = Color.parseColor("#CC4A4F") + isAntiAlias = false + style = Paint.Style.FILL + } + // Bottom and right shadows + canvas.drawRect(left + 2f, bottom - 2f, right, bottom, shadowPaint) + canvas.drawRect(right - 2f, top + 2f, right, bottom - 2f, shadowPaint) + } + "block_skin_4" -> { // Minimalist + // Draw clean, simple block with subtle border + blockPaint.color = if (isGhost) Color.argb(30, 0, 0, 0) else Color.BLACK + blockPaint.alpha = if (isGhost) 30 else 255 + blockPaint.style = Paint.Style.FILL + canvas.drawRect(left, top, right, bottom, blockPaint) + + // Draw subtle border + val borderPaint = Paint().apply { + color = Color.parseColor("#333333") + style = Paint.Style.STROKE + strokeWidth = 1f + isAntiAlias = true + } + canvas.drawRect(left, top, right, bottom, borderPaint) + } + "block_skin_5" -> { // Galaxy + // Draw cosmic glow effect + blockGlowPaint.color = if (isGhost) Color.argb(30, 102, 252, 241) else Color.parseColor("#66FCF1") + blockGlowPaint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.OUTER) + canvas.drawRect(left - 8f, top - 8f, right + 8f, bottom + 8f, blockGlowPaint) + + // Draw main block with gradient + val gradient = LinearGradient( + left, top, right, bottom, + Color.parseColor("#66FCF1"), + Color.parseColor("#45B7AF"), + Shader.TileMode.CLAMP + ) + blockPaint.shader = gradient + blockPaint.color = if (isGhost) Color.argb(30, 102, 252, 241) else Color.parseColor("#66FCF1") + blockPaint.alpha = if (isGhost) 30 else 255 + blockPaint.style = Paint.Style.FILL + canvas.drawRect(left, top, right, bottom, blockPaint) + + // Draw star-like sparkles + if (!isGhost) { + val sparklePaint = Paint().apply { + color = Color.WHITE + style = Paint.Style.FILL + isAntiAlias = true + maskFilter = BlurMaskFilter(4f, BlurMaskFilter.Blur.NORMAL) + } + // Add small white dots for sparkle effect + canvas.drawCircle(left + 4f, top + 4f, 1f, sparklePaint) + canvas.drawCircle(right - 4f, bottom - 4f, 1f, sparklePaint) + } } - canvas.drawRect(left, top, right, bottom, borderPaint) - - // Inner glow for neon blocks - brighter than before - glowPaint.color = if (isGhost) Color.argb(10, 255, 0, 255) else Color.parseColor("#FF00FF") - glowPaint.alpha = if (isGhost) 10 else 100 // More visible inner glow - glowPaint.style = Paint.Style.STROKE - glowPaint.strokeWidth = 2f - glowPaint.maskFilter = BlurMaskFilter(4f, BlurMaskFilter.Blur.NORMAL) - canvas.drawRect(left + 4f, top + 4f, right - 4f, bottom - 4f, glowPaint) - } else { - // Standard rendering for other skins - // Draw outer glow - blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE - canvas.drawRect(left - 2f, top - 2f, right + 2f, bottom + 2f, blockGlowPaint) - - // Draw block with current skin - blockPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else blockPaint.color - blockPaint.alpha = if (isGhost) 30 else 255 - canvas.drawRect(left, top, right, bottom, blockPaint) - - // Draw inner glow - glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE - canvas.drawRect(left + 1f, top + 1f, right - 1f, bottom - 1f, glowPaint) } // Draw pulse effect if animation is active and this is a pulsing line From ebff618fa464b270524e1a7206e6d871f4264afe Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:19:17 -0400 Subject: [PATCH 03/11] fix: balance progression system - Increased base XP requirements and curve - Added diminishing returns for higher levels - Reduced XP rewards for special moves - Capped level multiplier at level 10 - Added time-based XP reduction --- .../mintris/model/PlayerProgressionManager.kt | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index 7f95acc..17fcb1d 100644 --- a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt @@ -5,6 +5,7 @@ import android.content.SharedPreferences import com.mintris.R import kotlin.math.pow import kotlin.math.roundToInt +import kotlin.math.minOf /** * Manages player progression, experience points, and unlockable rewards @@ -94,21 +95,22 @@ class PlayerProgressionManager(context: Context) { */ fun calculateGameXP(score: Int, lines: Int, level: Int, gameTime: Long, tetrisCount: Int, perfectClearCount: Int): Long { - // Base XP from score with level multiplier - val scoreXP = (score * (1 + LEVEL_MULTIPLIER * level)).toLong() + // Base XP from score with level multiplier (capped at level 10) + val cappedLevel = minOf(level, 10) + val scoreXP = (score * (1 + LEVEL_MULTIPLIER * cappedLevel)).toLong() - // XP from lines cleared - val linesXP = lines * XP_PER_LINE + // XP from lines cleared (reduced for higher levels) + val linesXP = lines * XP_PER_LINE * (1 - (level - 1) * 0.05).coerceAtLeast(0.5) - // XP from special moves - val tetrisBonus = tetrisCount * TETRIS_XP_BONUS - val perfectClearBonus = perfectClearCount * PERFECT_CLEAR_XP_BONUS + // 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) - // Time bonus (to reward longer gameplay) - val timeBonus = (gameTime / 60000) * TIME_XP_PER_MINUTE // XP per minute played + // 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 + return (scoreXP + linesXP + tetrisBonus + perfectClearBonus + timeBonus).toLong() } /** @@ -285,13 +287,13 @@ class PlayerProgressionManager(context: Context) { private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin" // XP constants - private const val BASE_XP = 1000L - private const val XP_CURVE_FACTOR = 1.5 - private const val LEVEL_MULTIPLIER = 0.1 - private const val XP_PER_LINE = 100L - private const val TETRIS_XP_BONUS = 500L - private const val PERFECT_CLEAR_XP_BONUS = 1000L - private const val TIME_XP_PER_MINUTE = 50L + private const val BASE_XP = 2000L + private const val XP_CURVE_FACTOR = 1.8 + private const val LEVEL_MULTIPLIER = 0.05 + private const val XP_PER_LINE = 50L + private const val TETRIS_XP_BONUS = 200L + private const val PERFECT_CLEAR_XP_BONUS = 400L + private const val TIME_XP_PER_MINUTE = 25L // Theme constants const val THEME_CLASSIC = "theme_classic" From af0082a6dbdb42fa374cd94688c7d2aadbca0441 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:21:25 -0400 Subject: [PATCH 04/11] refactor: modernize codebase - Use KTX extension functions for system services - Update performClick handling in touch events - Modernize back gesture handling with KTX - Improve vibrator service initialization --- app/src/main/java/com/mintris/MainActivity.kt | 42 +++++++++---------- .../main/java/com/mintris/game/GameHaptics.kt | 21 +++++----- .../main/java/com/mintris/game/GameView.kt | 15 +++---- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index c084f6c..e16ef0b 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -688,29 +688,27 @@ class MainActivity : AppCompatActivity() { // Add an on back pressed callback to handle back button/gesture if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - onBackPressedDispatcher.addCallback(this, object : androidx.activity.OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - // If we're playing the game, handle it as a pause action instead of exiting - if (gameView.visibility == View.VISIBLE && !gameView.isPaused && !gameView.isGameOver()) { - gameView.pause() - gameMusic.pause() - showPauseMenu() - binding.pauseStartButton.visibility = View.GONE - binding.resumeButton.visibility = View.VISIBLE - } else if (binding.pauseContainer.visibility == View.VISIBLE) { - // If pause menu is showing, handle as a resume - resumeGame() - } else if (binding.gameOverContainer.visibility == View.VISIBLE) { - // If game over is showing, go back to title - hideGameOver() - showTitleScreen() - } else if (titleScreen.visibility == View.VISIBLE) { - // If title screen is showing, allow normal back behavior (exit app) - isEnabled = false - onBackPressedDispatcher.onBackPressed() - } + onBackPressedDispatcher.addCallback(this) { + // If we're playing the game, handle it as a pause action instead of exiting + if (gameView.visibility == View.VISIBLE && !gameView.isPaused && !gameView.isGameOver()) { + gameView.pause() + gameMusic.pause() + showPauseMenu() + binding.pauseStartButton.visibility = View.GONE + binding.resumeButton.visibility = View.VISIBLE + } else if (binding.pauseContainer.visibility == View.VISIBLE) { + // If pause menu is showing, handle as a resume + resumeGame() + } else if (binding.gameOverContainer.visibility == View.VISIBLE) { + // If game over is showing, go back to title + hideGameOver() + showTitleScreen() + } else if (titleScreen.visibility == View.VISIBLE) { + // If title screen is showing, allow normal back behavior (exit app) + isEnabled = false + onBackPressedDispatcher.onBackPressed() } - }) + } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // For Android 11 (R) to Android 12 (S), use the WindowInsetsController to disable gestures window.insetsController?.systemBarsBehavior = diff --git a/app/src/main/java/com/mintris/game/GameHaptics.kt b/app/src/main/java/com/mintris/game/GameHaptics.kt index 44e5011..7a46482 100644 --- a/app/src/main/java/com/mintris/game/GameHaptics.kt +++ b/app/src/main/java/com/mintris/game/GameHaptics.kt @@ -5,26 +5,27 @@ import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager +import android.util.Log import android.view.HapticFeedbackConstants import android.view.View -import android.util.Log +import androidx.core.content.getSystemService /** * Handles haptic feedback for game events */ class GameHaptics(private val context: Context) { - companion object { - private const val TAG = "GameHaptics" - } + private val TAG = "GameHaptics" // Vibrator service - private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager - vibratorManager.defaultVibrator - } else { - @Suppress("DEPRECATION") - context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + private val vibrator: Vibrator by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = context.getSystemService() + vibratorManager?.defaultVibrator ?: throw IllegalStateException("No vibrator available") + } else { + @Suppress("DEPRECATION") + context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } } // Vibrate for line clear (more intense for more lines) diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index e4d7625..74273f8 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -820,6 +820,7 @@ class GameView @JvmOverloads constructor( } } lastTapTime = currentTime + return true } MotionEvent.ACTION_MOVE -> { @@ -877,6 +878,7 @@ class GameView @JvmOverloads constructor( // No direction lock yet, don't process movement } } + return true } MotionEvent.ACTION_UP -> { @@ -909,21 +911,16 @@ class GameView @JvmOverloads constructor( } else if (moveTime < minTapTime && abs(deltaY) < maxTapMovement && abs(deltaX) < maxTapMovement) { - // Quick tap with minimal movement (rotation) - if (currentTime - lastRotationTime >= rotationCooldown) { - Log.d("GameView", "Rotation detected") - gameBoard.rotate() - lastRotationTime = currentTime - invalidate() - } + // This was a tap, perform click + performClick() } // Reset direction lock lockedDirection = null + return true } } - - return true + return super.onTouchEvent(event) } /** From d0700202b7c6e20fa982825990a1f75d6c978c14 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:41:03 -0400 Subject: [PATCH 05/11] Update game mechanics and haptic feedback implementation --- app/src/main/java/com/mintris/MainActivity.kt | 42 ++++++++++--------- .../main/java/com/mintris/game/GameHaptics.kt | 21 +++++----- .../main/java/com/mintris/game/GameView.kt | 15 ++++--- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index e16ef0b..c084f6c 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -688,27 +688,29 @@ class MainActivity : AppCompatActivity() { // Add an on back pressed callback to handle back button/gesture if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - onBackPressedDispatcher.addCallback(this) { - // If we're playing the game, handle it as a pause action instead of exiting - if (gameView.visibility == View.VISIBLE && !gameView.isPaused && !gameView.isGameOver()) { - gameView.pause() - gameMusic.pause() - showPauseMenu() - binding.pauseStartButton.visibility = View.GONE - binding.resumeButton.visibility = View.VISIBLE - } else if (binding.pauseContainer.visibility == View.VISIBLE) { - // If pause menu is showing, handle as a resume - resumeGame() - } else if (binding.gameOverContainer.visibility == View.VISIBLE) { - // If game over is showing, go back to title - hideGameOver() - showTitleScreen() - } else if (titleScreen.visibility == View.VISIBLE) { - // If title screen is showing, allow normal back behavior (exit app) - isEnabled = false - onBackPressedDispatcher.onBackPressed() + onBackPressedDispatcher.addCallback(this, object : androidx.activity.OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + // If we're playing the game, handle it as a pause action instead of exiting + if (gameView.visibility == View.VISIBLE && !gameView.isPaused && !gameView.isGameOver()) { + gameView.pause() + gameMusic.pause() + showPauseMenu() + binding.pauseStartButton.visibility = View.GONE + binding.resumeButton.visibility = View.VISIBLE + } else if (binding.pauseContainer.visibility == View.VISIBLE) { + // If pause menu is showing, handle as a resume + resumeGame() + } else if (binding.gameOverContainer.visibility == View.VISIBLE) { + // If game over is showing, go back to title + hideGameOver() + showTitleScreen() + } else if (titleScreen.visibility == View.VISIBLE) { + // If title screen is showing, allow normal back behavior (exit app) + isEnabled = false + onBackPressedDispatcher.onBackPressed() + } } - } + }) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // For Android 11 (R) to Android 12 (S), use the WindowInsetsController to disable gestures window.insetsController?.systemBarsBehavior = diff --git a/app/src/main/java/com/mintris/game/GameHaptics.kt b/app/src/main/java/com/mintris/game/GameHaptics.kt index 7a46482..44e5011 100644 --- a/app/src/main/java/com/mintris/game/GameHaptics.kt +++ b/app/src/main/java/com/mintris/game/GameHaptics.kt @@ -5,27 +5,26 @@ import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager -import android.util.Log import android.view.HapticFeedbackConstants import android.view.View -import androidx.core.content.getSystemService +import android.util.Log /** * Handles haptic feedback for game events */ class GameHaptics(private val context: Context) { - private val TAG = "GameHaptics" + companion object { + private const val TAG = "GameHaptics" + } // Vibrator service - private val vibrator: Vibrator by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val vibratorManager = context.getSystemService() - vibratorManager?.defaultVibrator ?: throw IllegalStateException("No vibrator available") - } else { - @Suppress("DEPRECATION") - context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - } + private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager + vibratorManager.defaultVibrator + } else { + @Suppress("DEPRECATION") + context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator } // Vibrate for line clear (more intense for more lines) diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index 74273f8..e4d7625 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -820,7 +820,6 @@ class GameView @JvmOverloads constructor( } } lastTapTime = currentTime - return true } MotionEvent.ACTION_MOVE -> { @@ -878,7 +877,6 @@ class GameView @JvmOverloads constructor( // No direction lock yet, don't process movement } } - return true } MotionEvent.ACTION_UP -> { @@ -911,16 +909,21 @@ class GameView @JvmOverloads constructor( } else if (moveTime < minTapTime && abs(deltaY) < maxTapMovement && abs(deltaX) < maxTapMovement) { - // This was a tap, perform click - performClick() + // Quick tap with minimal movement (rotation) + if (currentTime - lastRotationTime >= rotationCooldown) { + Log.d("GameView", "Rotation detected") + gameBoard.rotate() + lastRotationTime = currentTime + invalidate() + } } // Reset direction lock lockedDirection = null - return true } } - return super.onTouchEvent(event) + + return true } /** From 83935d35a81e21139041496caeeadac15cc3a390 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:43:40 -0400 Subject: [PATCH 06/11] Fix: Replace minOf with min in PlayerProgressionManager --- .../main/java/com/mintris/model/PlayerProgressionManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index 17fcb1d..2924d08 100644 --- a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt @@ -5,7 +5,7 @@ import android.content.SharedPreferences import com.mintris.R import kotlin.math.pow import kotlin.math.roundToInt -import kotlin.math.minOf +import kotlin.math.min /** * Manages player progression, experience points, and unlockable rewards @@ -96,7 +96,7 @@ class PlayerProgressionManager(context: Context) { fun calculateGameXP(score: Int, lines: Int, level: Int, gameTime: Long, tetrisCount: Int, perfectClearCount: Int): Long { // Base XP from score with level multiplier (capped at level 10) - val cappedLevel = minOf(level, 10) + val cappedLevel = min(level, 10) val scoreXP = (score * (1 + LEVEL_MULTIPLIER * cappedLevel)).toLong() // XP from lines cleared (reduced for higher levels) From c6a433993175224aa4c87d8783566ba75fde250d Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Fri, 28 Mar 2025 20:47:37 -0400 Subject: [PATCH 07/11] Fix: Update theme loading to use PlayerProgressionManager consistently across all activities --- app/src/main/java/com/mintris/HighScoreEntryActivity.kt | 3 +-- app/src/main/java/com/mintris/HighScoresActivity.kt | 3 +-- app/src/main/java/com/mintris/StatsActivity.kt | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/mintris/HighScoreEntryActivity.kt b/app/src/main/java/com/mintris/HighScoreEntryActivity.kt index 8829d52..5290cf6 100644 --- a/app/src/main/java/com/mintris/HighScoreEntryActivity.kt +++ b/app/src/main/java/com/mintris/HighScoreEntryActivity.kt @@ -66,8 +66,7 @@ class HighScoreEntryActivity : AppCompatActivity() { } private fun loadThemePreference(): String { - val prefs = getSharedPreferences("mintris_settings", MODE_PRIVATE) - return prefs.getString("selected_theme", PlayerProgressionManager.THEME_CLASSIC) ?: PlayerProgressionManager.THEME_CLASSIC + return progressionManager.getSelectedTheme() } private fun applyTheme(themeId: String) { diff --git a/app/src/main/java/com/mintris/HighScoresActivity.kt b/app/src/main/java/com/mintris/HighScoresActivity.kt index ae525bf..d91dfad 100644 --- a/app/src/main/java/com/mintris/HighScoresActivity.kt +++ b/app/src/main/java/com/mintris/HighScoresActivity.kt @@ -58,8 +58,7 @@ class HighScoresActivity : AppCompatActivity() { } private fun loadThemePreference(): String { - val prefs = getSharedPreferences("mintris_settings", MODE_PRIVATE) - return prefs.getString("selected_theme", PlayerProgressionManager.THEME_CLASSIC) ?: PlayerProgressionManager.THEME_CLASSIC + return progressionManager.getSelectedTheme() } private fun applyTheme(themeId: String) { diff --git a/app/src/main/java/com/mintris/StatsActivity.kt b/app/src/main/java/com/mintris/StatsActivity.kt index e7f6f3e..c7d2e0e 100644 --- a/app/src/main/java/com/mintris/StatsActivity.kt +++ b/app/src/main/java/com/mintris/StatsActivity.kt @@ -44,8 +44,7 @@ class StatsActivity : AppCompatActivity() { } private fun loadThemePreference(): String { - val prefs = getSharedPreferences("mintris_settings", MODE_PRIVATE) - return prefs.getString("selected_theme", PlayerProgressionManager.THEME_CLASSIC) ?: PlayerProgressionManager.THEME_CLASSIC + return progressionManager.getSelectedTheme() } private fun applyTheme(themeId: String) { From 1980f15a462bfc5b46204960e74927ba9545e5f3 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Sat, 29 Mar 2025 00:04:44 -0400 Subject: [PATCH 08/11] Adjust XP progression and improve animations: - Increased XP requirements and curve for slower leveling - Enhanced XP gain animations with smoother transitions - Improved visual feedback for level progression --- .../mintris/model/PlayerProgressionManager.kt | 14 +++--- .../java/com/mintris/ui/ProgressionScreen.kt | 45 +++++++++++-------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index 2924d08..0ddc2a7 100644 --- a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt @@ -287,13 +287,13 @@ class PlayerProgressionManager(context: Context) { private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin" // XP constants - private const val BASE_XP = 2000L - private const val XP_CURVE_FACTOR = 1.8 - private const val LEVEL_MULTIPLIER = 0.05 - private const val XP_PER_LINE = 50L - private const val TETRIS_XP_BONUS = 200L - private const val PERFECT_CLEAR_XP_BONUS = 400L - private const val TIME_XP_PER_MINUTE = 25L + private const val BASE_XP = 3000L + 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 TIME_XP_PER_MINUTE = 20L // Theme constants const val THEME_CLASSIC = "theme_classic" diff --git a/app/src/main/java/com/mintris/ui/ProgressionScreen.kt b/app/src/main/java/com/mintris/ui/ProgressionScreen.kt index bc67809..5aba75e 100644 --- a/app/src/main/java/com/mintris/ui/ProgressionScreen.kt +++ b/app/src/main/java/com/mintris/ui/ProgressionScreen.kt @@ -74,20 +74,35 @@ class ProgressionScreen @JvmOverloads constructor( playerLevelText.text = "Player Level: $playerLevel" xpGainText.text = "+$xpGained XP" - // Begin animation sequence - xpProgressBar.setXPValues(playerLevel, currentXP, xpForNextLevel) - - // Animate XP gain text entrance - val xpTextAnimator = ObjectAnimator.ofFloat(xpGainText, "alpha", 0f, 1f).apply { - duration = 500 + // Start with initial animations + AnimatorSet().apply { + // Fade in the XP gain text + val xpTextAnimator = ObjectAnimator.ofFloat(xpGainText, "alpha", 0f, 1f).apply { + duration = 800 + interpolator = AccelerateDecelerateInterpolator() + } + + // Set up the XP progress bar animation sequence + val xpBarAnimator = ObjectAnimator.ofFloat(xpProgressBar, "alpha", 0f, 1f).apply { + duration = 800 + interpolator = AccelerateDecelerateInterpolator() + } + + // Play animations in sequence + play(xpTextAnimator) + play(xpBarAnimator).after(xpTextAnimator) + start() } - // Schedule animation for the XP bar after text appears + // Set initial progress bar state + xpProgressBar.setXPValues(playerLevel, currentXP - xpGained, xpForNextLevel) + + // Animate the XP gain after a short delay postDelayed({ xpProgressBar.animateXPGain(xpGained, playerLevel, currentXP, xpForNextLevel) - }, 600) + }, 1000) // Increased delay to 1 second for better visual flow - // If there are new rewards, show them with animation + // If there are new rewards, show them with animation after XP bar animation if (newRewards.isNotEmpty()) { // Create reward cards rewardsContainer.removeAllViews() @@ -113,18 +128,12 @@ class ProgressionScreen @JvmOverloads constructor( card.animate() .alpha(1f) .translationY(0f) - .setDuration(400) - .setStartDelay((i * 150).toLong()) + .setDuration(600) // Increased duration for smoother animation + .setStartDelay((i * 200).toLong()) // Increased delay between cards .setInterpolator(OvershootInterpolator()) .start() } - }, 2000) // Wait for XP bar animation to finish - } - - // Start with initial animations - AnimatorSet().apply { - play(xpTextAnimator) - start() + }, 2500) // Increased delay to wait for XP bar animation to finish } } From 42b9bcfab4ea531e7bffc208630c4a2bfc0253e5 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Sat, 29 Mar 2025 01:58:53 -0400 Subject: [PATCH 09/11] fix: progression screen theme colors and XP bar styling --- .../java/com/mintris/ui/ProgressionScreen.kt | 53 ++++++++++--------- .../main/java/com/mintris/ui/XPProgressBar.kt | 2 + .../main/res/layout/progression_screen.xml | 3 +- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/mintris/ui/ProgressionScreen.kt b/app/src/main/java/com/mintris/ui/ProgressionScreen.kt index 5aba75e..8c5d4d4 100644 --- a/app/src/main/java/com/mintris/ui/ProgressionScreen.kt +++ b/app/src/main/java/com/mintris/ui/ProgressionScreen.kt @@ -31,6 +31,9 @@ class ProgressionScreen @JvmOverloads constructor( private val rewardsContainer: LinearLayout private val continueButton: TextView + // Current theme + private var currentTheme: String = PlayerProgressionManager.THEME_CLASSIC + // Callback for when the player dismisses the screen var onContinue: (() -> Unit)? = null @@ -62,6 +65,9 @@ class ProgressionScreen @JvmOverloads constructor( newRewards: List, themeId: String = PlayerProgressionManager.THEME_CLASSIC ) { + // Update current theme + currentTheme = themeId + // Hide rewards container initially if there are no new rewards rewardsContainer.visibility = if (newRewards.isEmpty()) View.GONE else View.INVISIBLE @@ -74,6 +80,10 @@ class ProgressionScreen @JvmOverloads constructor( playerLevelText.text = "Player Level: $playerLevel" xpGainText.text = "+$xpGained XP" + // Update level up text visibility + val progressionTitle = findViewById(R.id.progression_title) + progressionTitle.visibility = if (newRewards.any { it.contains("Level") }) View.VISIBLE else View.GONE + // Start with initial animations AnimatorSet().apply { // Fade in the XP gain text @@ -146,8 +156,17 @@ class ProgressionScreen @JvmOverloads constructor( cardElevation = 4f useCompatPadding = true - // Default background color - will be adjusted based on theme - setCardBackgroundColor(Color.BLACK) + // Set background color based on current theme + val backgroundColor = when (currentTheme) { + PlayerProgressionManager.THEME_CLASSIC -> Color.BLACK + PlayerProgressionManager.THEME_NEON -> Color.parseColor("#0D0221") + PlayerProgressionManager.THEME_MONOCHROME -> Color.parseColor("#1A1A1A") + PlayerProgressionManager.THEME_RETRO -> Color.parseColor("#3F2832") + PlayerProgressionManager.THEME_MINIMALIST -> Color.WHITE + PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#0B0C10") + else -> Color.BLACK + } + setCardBackgroundColor(backgroundColor) layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, @@ -176,6 +195,8 @@ class ProgressionScreen @JvmOverloads constructor( * Apply the current theme to the progression screen */ fun applyTheme(themeId: String) { + currentTheme = themeId + // Get reference to the title text val progressionTitle = findViewById(R.id.progression_title) val rewardsTitle = findViewById(R.id.rewards_title) @@ -257,10 +278,10 @@ class ProgressionScreen @JvmOverloads constructor( } } - // Set theme color on XP progress bar + // Update XP progress bar theme color xpProgressBar.setThemeColor(xpThemeColor) - // Update card colors for any existing reward cards + // Update reward card colors updateRewardCardColors(themeId) } @@ -268,8 +289,7 @@ class ProgressionScreen @JvmOverloads constructor( * Update colors of existing reward cards to match the theme */ private fun updateRewardCardColors(themeId: String) { - // Color for card backgrounds based on theme - val cardBackgroundColor = when (themeId) { + val backgroundColor = when (themeId) { PlayerProgressionManager.THEME_CLASSIC -> Color.BLACK PlayerProgressionManager.THEME_NEON -> Color.parseColor("#0D0221") PlayerProgressionManager.THEME_MONOCHROME -> Color.parseColor("#1A1A1A") @@ -279,28 +299,9 @@ class ProgressionScreen @JvmOverloads constructor( else -> Color.BLACK } - // Text color for rewards based on theme - val rewardTextColor = when (themeId) { - PlayerProgressionManager.THEME_CLASSIC -> Color.WHITE - PlayerProgressionManager.THEME_NEON -> Color.parseColor("#FF00FF") - PlayerProgressionManager.THEME_MONOCHROME -> Color.LTGRAY - PlayerProgressionManager.THEME_RETRO -> Color.parseColor("#FF5A5F") - PlayerProgressionManager.THEME_MINIMALIST -> Color.BLACK - PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#66FCF1") - else -> Color.WHITE - } - - // Update each card in the rewards container for (i in 0 until rewardsContainer.childCount) { val card = rewardsContainer.getChildAt(i) as? CardView - card?.let { - it.setCardBackgroundColor(cardBackgroundColor) - - // Update text color in the card - if (it.childCount > 0 && it.getChildAt(0) is TextView) { - (it.getChildAt(0) as TextView).setTextColor(rewardTextColor) - } - } + card?.setCardBackgroundColor(backgroundColor) } } } \ No newline at end of file diff --git a/app/src/main/java/com/mintris/ui/XPProgressBar.kt b/app/src/main/java/com/mintris/ui/XPProgressBar.kt index c50ab6d..930da2f 100644 --- a/app/src/main/java/com/mintris/ui/XPProgressBar.kt +++ b/app/src/main/java/com/mintris/ui/XPProgressBar.kt @@ -99,6 +99,8 @@ class XPProgressBar @JvmOverloads constructor( */ fun setThemeColor(color: Int) { themeColor = color + progressPaint.color = color + textPaint.color = color levelBadgePaint.color = color invalidate() } diff --git a/app/src/main/res/layout/progression_screen.xml b/app/src/main/res/layout/progression_screen.xml index 54b7fbf..b911769 100644 --- a/app/src/main/res/layout/progression_screen.xml +++ b/app/src/main/res/layout/progression_screen.xml @@ -3,8 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="16dp" - android:background="@color/black"> + android:padding="16dp"> Date: Sat, 29 Mar 2025 01:59:14 -0400 Subject: [PATCH 10/11] fix: theme colors and UI improvements --- app/src/main/java/com/mintris/MainActivity.kt | 135 +++++++++--------- .../main/java/com/mintris/game/GameView.kt | 23 +++ .../main/java/com/mintris/game/TitleScreen.kt | 44 ++++-- 3 files changed, 124 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index c084f6c..3e94bac 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -91,6 +91,14 @@ class MainActivity : AppCompatActivity() { themeSelector = binding.themeSelector blockSkinSelector = binding.blockSkinSelector + // Set up progression screen + progressionScreen = binding.progressionScreen + progressionScreen.visibility = View.GONE + progressionScreen.onContinue = { + progressionScreen.visibility = View.GONE + binding.gameOverContainer.visibility = View.VISIBLE + } + // Load and apply theme preference currentTheme = progressionManager.getSelectedTheme() applyTheme(currentTheme) @@ -109,14 +117,6 @@ class MainActivity : AppCompatActivity() { gameView.setGameBoard(gameBoard) gameView.setHaptics(gameHaptics) - // Set up progression screen - progressionScreen = binding.progressionScreen - progressionScreen.visibility = View.GONE - progressionScreen.onContinue = { - progressionScreen.visibility = View.GONE - binding.gameOverContainer.visibility = View.VISIBLE - } - // Set up theme selector themeSelector.onThemeSelected = { themeId: String -> // Apply the new theme @@ -358,10 +358,7 @@ class MainActivity : AppCompatActivity() { var showingHighScore = false // Show progression screen first with XP animation - binding.gameOverContainer.visibility = View.GONE - progressionScreen.visibility = View.VISIBLE - progressionScreen.applyTheme(currentTheme) - progressionScreen.showProgress(progressionManager, xpGained, newRewards, currentTheme) + showProgressionScreen(xpGained, newRewards) // Override the continue button behavior if high score needs to be shown val originalOnContinue = progressionScreen.onContinue @@ -593,63 +590,55 @@ class MainActivity : AppCompatActivity() { * Apply a theme to the game */ private fun applyTheme(themeId: String) { - // Only apply if the theme is unlocked - if (!progressionManager.isThemeUnlocked(themeId)) return - - // Save the selected theme currentTheme = themeId + val themeColor = when (themeId) { + PlayerProgressionManager.THEME_CLASSIC -> Color.WHITE + PlayerProgressionManager.THEME_NEON -> Color.parseColor("#FF00FF") + PlayerProgressionManager.THEME_MONOCHROME -> Color.LTGRAY + PlayerProgressionManager.THEME_RETRO -> Color.parseColor("#FF5A5F") + PlayerProgressionManager.THEME_MINIMALIST -> Color.BLACK + PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#66FCF1") + else -> Color.WHITE + } + + // Get background color for the theme + val backgroundColor = when (themeId) { + PlayerProgressionManager.THEME_CLASSIC -> Color.BLACK + PlayerProgressionManager.THEME_NEON -> Color.parseColor("#0D0221") + PlayerProgressionManager.THEME_MONOCHROME -> Color.parseColor("#1A1A1A") + PlayerProgressionManager.THEME_RETRO -> Color.parseColor("#3F2832") + PlayerProgressionManager.THEME_MINIMALIST -> Color.WHITE + PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#0B0C10") + else -> Color.BLACK + } + + // Apply background color to root view + binding.root.setBackgroundColor(backgroundColor) + + // Apply theme color to title screen + titleScreen.setThemeColor(themeColor) + titleScreen.setBackgroundColor(backgroundColor) + + // Apply theme color to game over screen + binding.gameOverContainer.setBackgroundColor(backgroundColor) + binding.gameOverText.setTextColor(themeColor) + binding.scoreText.setTextColor(themeColor) + binding.currentLevelText.setTextColor(themeColor) + binding.linesText.setTextColor(themeColor) + binding.comboText.setTextColor(themeColor) + binding.playAgainButton.setTextColor(themeColor) + binding.playAgainButton.setBackgroundResource(android.R.color.transparent) + binding.playAgainButton.setTextSize(24f) + + // Apply theme to progression screen (it will handle its own colors) + progressionScreen.applyTheme(themeId) + + // Apply theme color to game view + gameView.setThemeColor(themeColor) + gameView.setBackgroundColor(backgroundColor) + + // Save theme preference progressionManager.setSelectedTheme(themeId) - - // Apply theme to title screen if it's visible - if (titleScreen.visibility == View.VISIBLE) { - titleScreen.applyTheme(themeId) - } - - // Apply theme colors based on theme ID - when (themeId) { - PlayerProgressionManager.THEME_CLASSIC -> { - // Default black theme - binding.root.setBackgroundColor(Color.BLACK) - } - PlayerProgressionManager.THEME_NEON -> { - // Neon theme with dark purple background - binding.root.setBackgroundColor(Color.parseColor("#0D0221")) - } - PlayerProgressionManager.THEME_MONOCHROME -> { - // Monochrome dark gray - binding.root.setBackgroundColor(Color.parseColor("#1A1A1A")) - } - PlayerProgressionManager.THEME_RETRO -> { - // Retro arcade theme - binding.root.setBackgroundColor(Color.parseColor("#3F2832")) - } - PlayerProgressionManager.THEME_MINIMALIST -> { - // Minimalist white theme - binding.root.setBackgroundColor(Color.WHITE) - - // Update text colors for visibility - binding.scoreText.setTextColor(Color.BLACK) - binding.currentLevelText.setTextColor(Color.BLACK) - binding.linesText.setTextColor(Color.BLACK) - binding.comboText.setTextColor(Color.BLACK) - } - PlayerProgressionManager.THEME_GALAXY -> { - // Galaxy dark blue theme - binding.root.setBackgroundColor(Color.parseColor("#0B0C10")) - } - } - - // Apply theme to progression screen if it's visible and initialized - if (::progressionScreen.isInitialized && progressionScreen.visibility == View.VISIBLE) { - progressionScreen.applyTheme(themeId) - } - - // Apply theme color to the stats button - val textColor = getThemeColor(currentTheme) - binding.statsButton.setTextColor(textColor) - - // Update the game view to apply theme - gameView.invalidate() } /** @@ -745,4 +734,16 @@ class MainActivity : AppCompatActivity() { } return super.onKeyDown(keyCode, event) } + + private fun showProgressionScreen(xpGained: Long, newRewards: List) { + // Apply theme before showing the screen + progressionScreen.applyTheme(currentTheme) + + // Show the progression screen + binding.gameOverContainer.visibility = View.GONE + progressionScreen.visibility = View.VISIBLE + + // Display progression data + progressionScreen.showProgress(progressionManager, xpGained, newRewards, currentTheme) + } } \ No newline at end of file diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index e4d7625..1095af8 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -153,6 +153,7 @@ class GameView @JvmOverloads constructor( // Block skin private var currentBlockSkin: String = "block_skin_1" private val blockSkinPaints = mutableMapOf() + private var currentThemeColor = Color.WHITE private enum class Direction { HORIZONTAL, VERTICAL @@ -1050,4 +1051,26 @@ class GameView @JvmOverloads constructor( } pulseAnimator?.start() } + + /** + * Set the theme color for the game view + */ + fun setThemeColor(color: Int) { + currentThemeColor = color + blockPaint.color = color + ghostBlockPaint.color = color + glowPaint.color = color + blockGlowPaint.color = color + borderGlowPaint.color = color + pulsePaint.color = color + invalidate() + } + + /** + * Set the background color for the game view + */ + override fun setBackgroundColor(color: Int) { + super.setBackgroundColor(color) + invalidate() + } } diff --git a/app/src/main/java/com/mintris/game/TitleScreen.kt b/app/src/main/java/com/mintris/game/TitleScreen.kt index 4c7a80f..34627f7 100644 --- a/app/src/main/java/com/mintris/game/TitleScreen.kt +++ b/app/src/main/java/com/mintris/game/TitleScreen.kt @@ -46,8 +46,9 @@ class TitleScreen @JvmOverloads constructor( // Callback for when the user touches the screen var onStartGame: (() -> Unit)? = null - // Theme color + // Theme color and background color private var themeColor = Color.WHITE + private var backgroundColor = Color.BLACK // Define tetromino shapes (I, O, T, S, Z, J, L) private val tetrominoShapes = arrayOf( @@ -110,7 +111,7 @@ class TitleScreen @JvmOverloads constructor( init { // Title text settings titlePaint.apply { - color = Color.WHITE + color = themeColor textSize = 120f textAlign = Paint.Align.CENTER typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD) @@ -119,7 +120,7 @@ class TitleScreen @JvmOverloads constructor( // "Touch to start" text settings promptPaint.apply { - color = Color.WHITE + color = themeColor textSize = 50f textAlign = Paint.Align.CENTER typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL) @@ -129,7 +130,7 @@ class TitleScreen @JvmOverloads constructor( // High scores text settings highScorePaint.apply { - color = Color.WHITE + color = themeColor textSize = 70f textAlign = Paint.Align.LEFT // Changed to LEFT alignment typeface = Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL) // Changed to monospace @@ -137,16 +138,16 @@ class TitleScreen @JvmOverloads constructor( alpha = 200 } - // General paint settings for tetrominos (white) + // General paint settings for tetrominos paint.apply { - color = Color.WHITE + color = themeColor style = Paint.Style.FILL isAntiAlias = true } // Glow paint settings for tetrominos glowPaint.apply { - color = Color.WHITE + color = themeColor style = Paint.Style.FILL isAntiAlias = true alpha = 60 @@ -184,8 +185,8 @@ class TitleScreen @JvmOverloads constructor( try { super.onDraw(canvas) - // Draw background - canvas.drawColor(Color.BLACK) + // Draw background using the current background color + canvas.drawColor(backgroundColor) // Add any pending tetrominos tetrominos.addAll(tetrominosToAdd) @@ -340,7 +341,7 @@ class TitleScreen @JvmOverloads constructor( glowPaint.color = themeColor // Update background color - setBackgroundColor(when (themeId) { + backgroundColor = when (themeId) { PlayerProgressionManager.THEME_CLASSIC -> Color.BLACK PlayerProgressionManager.THEME_NEON -> Color.parseColor("#0D0221") PlayerProgressionManager.THEME_MONOCHROME -> Color.parseColor("#1A1A1A") @@ -348,8 +349,29 @@ class TitleScreen @JvmOverloads constructor( PlayerProgressionManager.THEME_MINIMALIST -> Color.WHITE PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#0B0C10") else -> Color.BLACK - }) + } invalidate() } + + /** + * Set the theme color for the title screen + */ + fun setThemeColor(color: Int) { + themeColor = color + titlePaint.color = color + promptPaint.color = color + highScorePaint.color = color + paint.color = color + glowPaint.color = color + invalidate() + } + + /** + * Set the background color for the title screen + */ + override fun setBackgroundColor(color: Int) { + backgroundColor = color + invalidate() + } } \ No newline at end of file From 103a21d9b74b401694f517fa2618d43d1a51fc36 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Sat, 29 Mar 2025 02:21:15 -0400 Subject: [PATCH 11/11] fix: update text styles and sizes across app to match mintris theme --- app/src/main/res/layout/activity_main.xml | 136 +++++++++++++----- .../main/res/layout/block_skin_selector.xml | 5 +- .../main/res/layout/progression_screen.xml | 29 ++-- app/src/main/res/layout/theme_selector.xml | 5 +- app/src/main/res/values/strings.xml | 88 ++++++------ 5 files changed, 165 insertions(+), 98 deletions(-) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ddea78a..2224e92 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -62,36 +62,40 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/white" - android:textSize="24sp" - android:fontFamily="sans-serif-light" - tools:text="Score: 0" /> + android:textSize="32sp" + android:textStyle="bold" + android:fontFamily="sans-serif" + tools:text="score: 0" /> + android:textSize="32sp" + android:textStyle="bold" + android:fontFamily="sans-serif" + tools:text="level: 1" /> + android:textSize="32sp" + android:textStyle="bold" + android:fontFamily="sans-serif" + tools:text="lines: 0" /> + android:textSize="32sp" + android:textStyle="bold" + android:fontFamily="sans-serif" + tools:text="combo: 0" /> @@ -131,16 +135,18 @@ android:layout_height="wrap_content" android:text="@string/game_over" android:textColor="@color/white" - android:textSize="24sp" - android:textStyle="bold" /> + android:textSize="36sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" /> + android:textSize="24sp" + android:textStyle="bold" + android:fontFamily="sans-serif" />