diff --git a/app/src/main/java/com/pixelmintdrop/MainActivity.kt b/app/src/main/java/com/pixelmintdrop/MainActivity.kt index 5096312..4ae1f16 100644 --- a/app/src/main/java/com/pixelmintdrop/MainActivity.kt +++ b/app/src/main/java/com/pixelmintdrop/MainActivity.kt @@ -471,7 +471,7 @@ class MainActivity : AppCompatActivity(), score = score, lines = gameBoard.lines, level = currentLevel, - gameTime = gameTime, + timePlayedMs = gameTime, quadCount = statsManager.getSessionQuads(), perfectClearCount = 0 // Implement perfect clear tracking if needed ) @@ -496,7 +496,7 @@ class MainActivity : AppCompatActivity(), binding.sessionSinglesText.text = getString(R.string.singles, statsManager.getSessionSingles()) binding.sessionDoublesText.text = getString(R.string.doubles, statsManager.getSessionDoubles()) binding.sessionTriplesText.text = getString(R.string.triples, statsManager.getSessionTriples()) - binding.sessionQuadsText.text = getString(R.string.quads, statsManager.getSessionQuads()) + binding.sessionQuadsText?.text = getString(R.string.quads, statsManager.getSessionQuads()) // Flag to track if high score screen will be shown var showingHighScore = false diff --git a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt index 1652365..e0f7aa6 100644 --- a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt +++ b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt @@ -78,7 +78,7 @@ class StatsActivity : AppCompatActivity() { binding.totalSinglesText.setTextColor(textColor) binding.totalDoublesText.setTextColor(textColor) binding.totalTriplesText.setTextColor(textColor) - binding.totalQuadsText.setTextColor(textColor) + binding.totalQuadsText?.setTextColor(textColor) binding.maxLevelText.setTextColor(textColor) binding.maxScoreText.setTextColor(textColor) binding.maxLinesText.setTextColor(textColor) @@ -116,7 +116,7 @@ class StatsActivity : AppCompatActivity() { binding.totalSinglesText.text = getString(R.string.singles, statsManager.getTotalSingles()) binding.totalDoublesText.text = getString(R.string.doubles, statsManager.getTotalDoubles()) binding.totalTriplesText.text = getString(R.string.triples, statsManager.getTotalTriples()) - binding.totalQuadsText.text = getString(R.string.quads, statsManager.getTotalQuads()) + binding.totalQuadsText?.text = getString(R.string.quads, statsManager.getTotalQuads()) // Update best performance stats binding.maxLevelText.text = getString(R.string.max_level, statsManager.getMaxLevel()) diff --git a/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt b/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt index 7529fef..395e4bb 100644 --- a/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/pixelmintdrop/model/PlayerProgressionManager.kt @@ -5,6 +5,10 @@ import android.content.SharedPreferences import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.min +import java.math.BigDecimal +import java.math.BigInteger +import kotlin.math.max +import com.pixelmintdrop.R // Assuming themes are defined elsewhere, maybe R? /** * Manages player progression, experience points, and unlockable rewards @@ -95,13 +99,23 @@ class PlayerProgressionManager(context: Context) { */ fun calculateGameXP(score: Int, lines: Int, level: Int, timePlayedMs: Long, quadCount: Int, perfectClearCount: Int): Long { - val scoreXP = score * SCORE_XP_MULTIPLIER - val linesXP = lines * LINES_XP_MULTIPLIER * level - val quadBonus = quadCount * QUAD_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5) - val perfectClearBonus = perfectClearCount * PERFECT_CLEAR_XP_BONUS - val timeBonus = (timePlayedMs / 1000) * TIME_XP_MULTIPLIER + // Use Double for calculations to maintain precision with multipliers + val scoreXP = score.toDouble() * SCORE_XP_MULTIPLIER + // Lines XP scales with level + val linesXP = lines.toDouble() * LINES_XP_MULTIPLIER * level.toDouble() + // Quad bonus scales inversely with level (harder quads at higher levels mean less relative bonus?) - capped at 0.5 minimum multiplier + val quadBonusMultiplier = (1.0 - (level - 1).toDouble() * 0.05).coerceAtLeast(0.5) + val quadBonus = quadCount.toDouble() * QUAD_XP_BONUS * quadBonusMultiplier + // Perfect clear bonus is a fixed Long value + val perfectClearBonusXP = perfectClearCount * PERFECT_CLEAR_XP_BONUS // This is already Long + // Time bonus based on seconds played + val timeBonus = (timePlayedMs / 1000.0) * TIME_XP_MULTIPLIER - return (scoreXP + linesXP + quadBonus + perfectClearBonus + timeBonus).toLong() + // Summing up components (ensure consistent types, cast bonus to Double if needed) + val totalXP = scoreXP + linesXP + quadBonus + perfectClearBonusXP.toDouble() + timeBonus + + // Return the final XP as Long + return max(0L, totalXP.toLong()) // Ensure XP is not negative and return Long } /** @@ -126,12 +140,12 @@ class PlayerProgressionManager(context: Context) { val levelRewards = checkLevelRewards(playerLevel) newRewards.addAll(levelRewards) - // Calculate XP needed for the next level + // Calculate XP needed for the *new* next level xpForNextLevel = calculateXPForLevel(playerLevel) } - // Save progress if there were any changes - if (oldLevel != playerLevel || newRewards.isNotEmpty()) { + // Save progress if level changed or rewards were unlocked + if (oldLevel != playerLevel || newRewards.isNotEmpty() || xpAmount > 0) { saveProgress() } @@ -143,32 +157,38 @@ class PlayerProgressionManager(context: Context) { */ private fun checkLevelRewards(level: Int): List { val newRewards = mutableListOf() + var newlyUnlocked = false // Track if anything was actually unlocked in this call // Check for theme unlocks when (level) { 5 -> { if (unlockedThemes.add(THEME_NEON)) { newRewards.add("Unlocked Neon Theme!") + newlyUnlocked = true } } 10 -> { if (unlockedThemes.add(THEME_MONOCHROME)) { newRewards.add("Unlocked Monochrome Theme!") + newlyUnlocked = true } } 15 -> { if (unlockedThemes.add(THEME_RETRO)) { newRewards.add("Unlocked Retro Arcade Theme!") + newlyUnlocked = true } } 20 -> { if (unlockedThemes.add(THEME_MINIMALIST)) { newRewards.add("Unlocked Minimalist Theme!") + newlyUnlocked = true } } 25 -> { if (unlockedThemes.add(THEME_GALAXY)) { newRewards.add("Unlocked Galaxy Theme!") + newlyUnlocked = true } } } @@ -178,25 +198,34 @@ class PlayerProgressionManager(context: Context) { 7 -> { if (unlockedBlocks.add("block_skin_2")) { newRewards.add("Unlocked Neon Block Skin!") + newlyUnlocked = true } } 14 -> { if (unlockedBlocks.add("block_skin_3")) { newRewards.add("Unlocked Retro Block Skin!") + newlyUnlocked = true } } 21 -> { if (unlockedBlocks.add("block_skin_4")) { newRewards.add("Unlocked Minimalist Block Skin!") + newlyUnlocked = true } } 28 -> { if (unlockedBlocks.add("block_skin_5")) { newRewards.add("Unlocked Galaxy Block Skin!") + newlyUnlocked = true } } } + // Save progress immediately if something was unlocked by this check + if (newlyUnlocked) { + saveProgress() + } + return newRewards } @@ -238,9 +267,10 @@ class PlayerProgressionManager(context: Context) { fun getCurrentXP(): Long = playerXP fun getXPForNextLevel(): Long = calculateXPForLevel(playerLevel) fun getSessionXPGained(): Long = sessionXPGained - fun getUnlockedThemes(): Set = unlockedThemes.toSet() - fun getUnlockedBlocks(): Set = unlockedBlocks.toSet() - fun getUnlockedBadges(): Set = unlockedBadges.toSet() + fun getTotalXPEarned(): Long = totalXPEarned // Added getter for total XP + fun getUnlockedThemes(): Set = unlockedThemes.toSet() // Return immutable copy + fun getUnlockedBlocks(): Set = unlockedBlocks.toSet() // Return immutable copy + fun getUnlockedBadges(): Set = unlockedBadges.toSet() // Return immutable copy /** * Check if a specific theme is unlocked @@ -288,7 +318,7 @@ class PlayerProgressionManager(context: Context) { private const val KEY_TOTAL_XP_EARNED = "total_xp_earned" 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_UNLOCKED_BADGES = "unlockedBadges" private const val KEY_SELECTED_THEME = "selected_theme" private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin" @@ -318,6 +348,16 @@ class PlayerProgressionManager(context: Context) { THEME_MINIMALIST to 20, THEME_GALAXY to 25 ) + + // XP Calculation Constants (Added) + private const val BASE_XP_PER_LEVEL = 1000.0 + private const val XP_LEVEL_MULTIPLIER = 1.15 // XP increases by 15% each level + + // Game XP Constants (Added) + private const val SCORE_XP_MULTIPLIER = 0.05 // 1 XP per 20 points + private const val LINES_XP_MULTIPLIER = 25.0 // Base XP per line + private const val QUAD_XP_BONUS = 150.0 // Bonus XP per Quad + private const val TIME_XP_MULTIPLIER = 0.5 // 0.5 XP per second played } /** @@ -358,4 +398,11 @@ class PlayerProgressionManager(context: Context) { fun getSelectedTheme(): String { return prefs.getString(KEY_SELECTED_THEME, THEME_CLASSIC) ?: THEME_CLASSIC } + + /** + * Check if a specific block skin is unlocked + */ + fun isBlockSkinUnlocked(blockSkinId: String): Boolean { + return unlockedBlocks.contains(blockSkinId) + } } \ 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 83deb42..3c1d658 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -266,7 +266,7 @@ android:fontFamily="sans-serif" />