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/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index 5390803..3e94bac 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -91,17 +91,6 @@ class MainActivity : AppCompatActivity() { themeSelector = binding.themeSelector blockSkinSelector = binding.blockSkinSelector - // Load and apply theme preference - currentTheme = progressionManager.getSelectedTheme() - applyTheme(currentTheme) - - // Load and apply block skin preference - gameView.setBlockSkin(progressionManager.getSelectedBlockSkin()) - - // Set up game view - gameView.setGameBoard(gameBoard) - gameView.setHaptics(gameHaptics) - // Set up progression screen progressionScreen = binding.progressionScreen progressionScreen.visibility = View.GONE @@ -110,6 +99,24 @@ class MainActivity : AppCompatActivity() { binding.gameOverContainer.visibility = View.VISIBLE } + // Load and apply theme preference + currentTheme = progressionManager.getSelectedTheme() + applyTheme(currentTheme) + + // 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) + // Set up theme selector themeSelector.onThemeSelected = { themeId: String -> // Apply the new theme @@ -351,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 @@ -586,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() } /** @@ -738,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/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) { diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index fe388b5..1095af8 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 @@ -151,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 @@ -173,6 +176,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 = { @@ -232,6 +239,7 @@ class GameView @JvmOverloads constructor( blockSkinPaints["block_skin_1"] = Paint().apply { color = Color.WHITE isAntiAlias = true + style = Paint.Style.FILL } // Neon skin @@ -244,9 +252,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 @@ -269,6 +276,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() } @@ -594,61 +604,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 @@ -963,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 diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index bf62a6a..0ddc2a7 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.min /** * 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 = min(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() } /** @@ -281,21 +283,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 = 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 - // 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 +326,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() } } diff --git a/app/src/main/java/com/mintris/ui/ProgressionScreen.kt b/app/src/main/java/com/mintris/ui/ProgressionScreen.kt index bc67809..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,20 +80,39 @@ class ProgressionScreen @JvmOverloads constructor( playerLevelText.text = "Player Level: $playerLevel" xpGainText.text = "+$xpGained XP" - // Begin animation sequence - xpProgressBar.setXPValues(playerLevel, currentXP, xpForNextLevel) + // 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 - // 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 +138,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 } } @@ -137,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, @@ -167,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) @@ -248,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) } @@ -259,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") @@ -270,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/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" />