mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-18 05:25:20 +01:00
Added block skin selection feature and removed powerups
This commit is contained in:
parent
7614cef7e5
commit
5861644883
6 changed files with 486 additions and 79 deletions
|
@ -19,6 +19,8 @@ import com.mintris.model.HighScoreManager
|
||||||
import com.mintris.model.PlayerProgressionManager
|
import com.mintris.model.PlayerProgressionManager
|
||||||
import com.mintris.model.StatsManager
|
import com.mintris.model.StatsManager
|
||||||
import com.mintris.ui.ProgressionScreen
|
import com.mintris.ui.ProgressionScreen
|
||||||
|
import com.mintris.ui.ThemeSelector
|
||||||
|
import com.mintris.ui.BlockSkinSelector
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
@ -45,6 +47,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var statsManager: StatsManager
|
private lateinit var statsManager: StatsManager
|
||||||
private lateinit var progressionManager: PlayerProgressionManager
|
private lateinit var progressionManager: PlayerProgressionManager
|
||||||
private lateinit var progressionScreen: ProgressionScreen
|
private lateinit var progressionScreen: ProgressionScreen
|
||||||
|
private lateinit var themeSelector: ThemeSelector
|
||||||
|
private lateinit var blockSkinSelector: BlockSkinSelector
|
||||||
|
|
||||||
// Game state
|
// Game state
|
||||||
private var isSoundEnabled = true
|
private var isSoundEnabled = true
|
||||||
|
@ -84,11 +88,16 @@ class MainActivity : AppCompatActivity() {
|
||||||
highScoreManager = HighScoreManager(this)
|
highScoreManager = HighScoreManager(this)
|
||||||
statsManager = StatsManager(this)
|
statsManager = StatsManager(this)
|
||||||
progressionManager = PlayerProgressionManager(this)
|
progressionManager = PlayerProgressionManager(this)
|
||||||
|
themeSelector = binding.themeSelector
|
||||||
|
blockSkinSelector = binding.blockSkinSelector
|
||||||
|
|
||||||
// Load and apply theme preference
|
// Load and apply theme preference
|
||||||
currentTheme = loadThemePreference()
|
currentTheme = progressionManager.getSelectedTheme()
|
||||||
applyTheme(currentTheme)
|
applyTheme(currentTheme)
|
||||||
|
|
||||||
|
// Load and apply block skin preference
|
||||||
|
gameView.setBlockSkin(progressionManager.getSelectedBlockSkin())
|
||||||
|
|
||||||
// Set up game view
|
// Set up game view
|
||||||
gameView.setGameBoard(gameBoard)
|
gameView.setGameBoard(gameBoard)
|
||||||
gameView.setHaptics(gameHaptics)
|
gameView.setHaptics(gameHaptics)
|
||||||
|
@ -102,8 +111,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up theme selector
|
// Set up theme selector
|
||||||
val themeSelector = binding.themeSelector
|
themeSelector.onThemeSelected = { themeId: String ->
|
||||||
themeSelector.onThemeSelected = { themeId ->
|
|
||||||
// Apply the new theme
|
// Apply the new theme
|
||||||
applyTheme(themeId)
|
applyTheme(themeId)
|
||||||
|
|
||||||
|
@ -116,6 +124,18 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up block skin selector
|
||||||
|
blockSkinSelector.onBlockSkinSelected = { skinId: String ->
|
||||||
|
// Apply the new block skin
|
||||||
|
gameView.setBlockSkin(skinId)
|
||||||
|
|
||||||
|
// Save the selection
|
||||||
|
progressionManager.setSelectedBlockSkin(skinId)
|
||||||
|
|
||||||
|
// Provide haptic feedback
|
||||||
|
gameHaptics.vibrateForPieceLock()
|
||||||
|
}
|
||||||
|
|
||||||
// Set up title screen
|
// Set up title screen
|
||||||
titleScreen.onStartGame = {
|
titleScreen.onStartGame = {
|
||||||
titleScreen.visibility = View.GONE
|
titleScreen.visibility = View.GONE
|
||||||
|
@ -426,6 +446,13 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// Update theme selector
|
// Update theme selector
|
||||||
updateThemeSelector()
|
updateThemeSelector()
|
||||||
|
|
||||||
|
// Update block skin selector
|
||||||
|
blockSkinSelector.updateBlockSkins(
|
||||||
|
progressionManager.getUnlockedBlocks(),
|
||||||
|
gameView.getCurrentBlockSkin(),
|
||||||
|
progressionManager.getPlayerLevel()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -564,7 +591,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// Save the selected theme
|
// Save the selected theme
|
||||||
currentTheme = themeId
|
currentTheme = themeId
|
||||||
saveThemePreference(themeId)
|
progressionManager.setSelectedTheme(themeId)
|
||||||
|
|
||||||
// Apply theme to title screen if it's visible
|
// Apply theme to title screen if it's visible
|
||||||
if (titleScreen.visibility == View.VISIBLE) {
|
if (titleScreen.visibility == View.VISIBLE) {
|
||||||
|
@ -618,22 +645,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
gameView.invalidate()
|
gameView.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the selected theme in preferences
|
|
||||||
*/
|
|
||||||
private fun saveThemePreference(themeId: String) {
|
|
||||||
val prefs = getSharedPreferences("mintris_settings", Context.MODE_PRIVATE)
|
|
||||||
prefs.edit().putString("selected_theme", themeId).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the saved theme preference
|
|
||||||
*/
|
|
||||||
private fun loadThemePreference(): String {
|
|
||||||
val prefs = getSharedPreferences("mintris_settings", Context.MODE_PRIVATE)
|
|
||||||
return prefs.getString("selected_theme", PlayerProgressionManager.THEME_CLASSIC) ?: PlayerProgressionManager.THEME_CLASSIC
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate color for the current theme
|
* Get the appropriate color for the current theme
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -144,6 +144,10 @@ class GameView @JvmOverloads constructor(
|
||||||
private val isStrictDirectionLock = true // Enable strict direction locking to prevent diagonal inputs
|
private val isStrictDirectionLock = true // Enable strict direction locking to prevent diagonal inputs
|
||||||
private val minHardDropDistance = 1.5f // Minimum distance (in blocks) for hard drop gesture
|
private val minHardDropDistance = 1.5f // Minimum distance (in blocks) for hard drop gesture
|
||||||
|
|
||||||
|
// Block skin
|
||||||
|
private var currentBlockSkin: String = "block_skin_1"
|
||||||
|
private val blockSkinPaints = mutableMapOf<String, Paint>()
|
||||||
|
|
||||||
private enum class Direction {
|
private enum class Direction {
|
||||||
HORIZONTAL, VERTICAL
|
HORIZONTAL, VERTICAL
|
||||||
}
|
}
|
||||||
|
@ -206,8 +210,64 @@ class GameView @JvmOverloads constructor(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
setSystemGestureExclusionRects(listOf(Rect(0, 0, width, height)))
|
setSystemGestureExclusionRects(listOf(Rect(0, 0, width, height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize block skin paints
|
||||||
|
initializeBlockSkinPaints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize paints for different block skins
|
||||||
|
*/
|
||||||
|
private fun initializeBlockSkinPaints() {
|
||||||
|
// Classic skin
|
||||||
|
blockSkinPaints["block_skin_1"] = Paint().apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
isAntiAlias = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neon skin
|
||||||
|
blockSkinPaints["block_skin_2"] = Paint().apply {
|
||||||
|
color = Color.parseColor("#FF00FF")
|
||||||
|
isAntiAlias = true
|
||||||
|
maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.OUTER)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retro skin
|
||||||
|
blockSkinPaints["block_skin_3"] = Paint().apply {
|
||||||
|
color = Color.parseColor("#FF5A5F")
|
||||||
|
isAntiAlias = true
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimalist skin
|
||||||
|
blockSkinPaints["block_skin_4"] = Paint().apply {
|
||||||
|
color = Color.BLACK
|
||||||
|
isAntiAlias = true
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Galaxy skin
|
||||||
|
blockSkinPaints["block_skin_5"] = Paint().apply {
|
||||||
|
color = Color.parseColor("#66FCF1")
|
||||||
|
isAntiAlias = true
|
||||||
|
maskFilter = BlurMaskFilter(12f, BlurMaskFilter.Blur.OUTER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current block skin
|
||||||
|
*/
|
||||||
|
fun setBlockSkin(skinId: String) {
|
||||||
|
currentBlockSkin = skinId
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current block skin
|
||||||
|
*/
|
||||||
|
fun getCurrentBlockSkin(): String = currentBlockSkin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the game
|
* Start the game
|
||||||
*/
|
*/
|
||||||
|
@ -519,16 +579,19 @@ class GameView @JvmOverloads constructor(
|
||||||
// Save canvas state before drawing block effects
|
// Save canvas state before drawing block effects
|
||||||
canvas.save()
|
canvas.save()
|
||||||
|
|
||||||
|
// Get the current block skin paint
|
||||||
|
val paint = blockSkinPaints[currentBlockSkin] ?: blockSkinPaints["block_skin_1"]!!
|
||||||
|
|
||||||
// Draw outer glow
|
// Draw outer glow
|
||||||
blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
||||||
canvas.drawRect(left - 2f, top - 2f, right + 2f, bottom + 2f, blockGlowPaint)
|
canvas.drawRect(left - 2f, top - 2f, right + 2f, bottom + 2f, blockGlowPaint)
|
||||||
|
|
||||||
// Draw block
|
// Draw block with current skin
|
||||||
blockPaint.apply {
|
paint.apply {
|
||||||
color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
color = if (isGhost) Color.argb(30, 255, 255, 255) else paint.color
|
||||||
alpha = if (isGhost) 30 else 255
|
alpha = if (isGhost) 30 else 255
|
||||||
}
|
}
|
||||||
canvas.drawRect(left, top, right, bottom, blockPaint)
|
canvas.drawRect(left, top, right, bottom, paint)
|
||||||
|
|
||||||
// Draw inner glow
|
// Draw inner glow
|
||||||
glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
||||||
|
|
|
@ -20,7 +20,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
// Track unlocked rewards
|
// Track unlocked rewards
|
||||||
private val unlockedThemes = mutableSetOf<String>()
|
private val unlockedThemes = mutableSetOf<String>()
|
||||||
private val unlockedBlocks = mutableSetOf<String>()
|
private val unlockedBlocks = mutableSetOf<String>()
|
||||||
private val unlockedPowers = mutableSetOf<String>()
|
|
||||||
private val unlockedBadges = mutableSetOf<String>()
|
private val unlockedBadges = mutableSetOf<String>()
|
||||||
|
|
||||||
// XP gained in the current session
|
// XP gained in the current session
|
||||||
|
@ -41,18 +40,21 @@ class PlayerProgressionManager(context: Context) {
|
||||||
// Load unlocked rewards
|
// Load unlocked rewards
|
||||||
val themesSet = prefs.getStringSet(KEY_UNLOCKED_THEMES, setOf()) ?: setOf()
|
val themesSet = prefs.getStringSet(KEY_UNLOCKED_THEMES, setOf()) ?: setOf()
|
||||||
val blocksSet = prefs.getStringSet(KEY_UNLOCKED_BLOCKS, setOf()) ?: setOf()
|
val blocksSet = prefs.getStringSet(KEY_UNLOCKED_BLOCKS, setOf()) ?: setOf()
|
||||||
val powersSet = prefs.getStringSet(KEY_UNLOCKED_POWERS, setOf()) ?: setOf()
|
|
||||||
val badgesSet = prefs.getStringSet(KEY_UNLOCKED_BADGES, setOf()) ?: setOf()
|
val badgesSet = prefs.getStringSet(KEY_UNLOCKED_BADGES, setOf()) ?: setOf()
|
||||||
|
|
||||||
unlockedThemes.addAll(themesSet)
|
unlockedThemes.addAll(themesSet)
|
||||||
unlockedBlocks.addAll(blocksSet)
|
unlockedBlocks.addAll(blocksSet)
|
||||||
unlockedPowers.addAll(powersSet)
|
|
||||||
unlockedBadges.addAll(badgesSet)
|
unlockedBadges.addAll(badgesSet)
|
||||||
|
|
||||||
// Add default theme if nothing is unlocked
|
// Add default theme if nothing is unlocked
|
||||||
if (unlockedThemes.isEmpty()) {
|
if (unlockedThemes.isEmpty()) {
|
||||||
unlockedThemes.add(THEME_CLASSIC)
|
unlockedThemes.add(THEME_CLASSIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add default block skin if nothing is unlocked
|
||||||
|
if (unlockedBlocks.isEmpty()) {
|
||||||
|
unlockedBlocks.add("block_skin_1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,7 +67,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
.putLong(KEY_TOTAL_XP_EARNED, totalXPEarned)
|
.putLong(KEY_TOTAL_XP_EARNED, totalXPEarned)
|
||||||
.putStringSet(KEY_UNLOCKED_THEMES, unlockedThemes)
|
.putStringSet(KEY_UNLOCKED_THEMES, unlockedThemes)
|
||||||
.putStringSet(KEY_UNLOCKED_BLOCKS, unlockedBlocks)
|
.putStringSet(KEY_UNLOCKED_BLOCKS, unlockedBlocks)
|
||||||
.putStringSet(KEY_UNLOCKED_POWERS, unlockedPowers)
|
|
||||||
.putStringSet(KEY_UNLOCKED_BADGES, unlockedBadges)
|
.putStringSet(KEY_UNLOCKED_BADGES, unlockedBadges)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
@ -179,30 +180,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for power unlocks
|
|
||||||
when (level) {
|
|
||||||
8 -> {
|
|
||||||
if (unlockedPowers.add(POWER_FREEZE_TIME)) {
|
|
||||||
newRewards.add("Unlocked Freeze Time Power!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12 -> {
|
|
||||||
if (unlockedPowers.add(POWER_BLOCK_SWAP)) {
|
|
||||||
newRewards.add("Unlocked Block Swap Power!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18 -> {
|
|
||||||
if (unlockedPowers.add(POWER_SAFE_LANDING)) {
|
|
||||||
newRewards.add("Unlocked Safe Landing Power!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
30 -> {
|
|
||||||
if (unlockedPowers.add(POWER_PERFECT_CLEAR)) {
|
|
||||||
newRewards.add("Unlocked Perfect Clear Power!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for block skin unlocks
|
// Check for block skin unlocks
|
||||||
if (level % 7 == 0 && level <= 35) {
|
if (level % 7 == 0 && level <= 35) {
|
||||||
val blockSkin = "block_skin_${level / 7}"
|
val blockSkin = "block_skin_${level / 7}"
|
||||||
|
@ -214,11 +191,38 @@ class PlayerProgressionManager(context: Context) {
|
||||||
return newRewards
|
return newRewards
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and unlock any rewards the player should have based on their current level
|
||||||
|
* This ensures players don't miss unlocks if they level up multiple times at once
|
||||||
|
*/
|
||||||
|
private fun checkAllUnlocksForCurrentLevel() {
|
||||||
|
// Check theme unlocks
|
||||||
|
if (playerLevel >= 5) unlockedThemes.add(THEME_NEON)
|
||||||
|
if (playerLevel >= 10) unlockedThemes.add(THEME_MONOCHROME)
|
||||||
|
if (playerLevel >= 15) unlockedThemes.add(THEME_RETRO)
|
||||||
|
if (playerLevel >= 20) unlockedThemes.add(THEME_MINIMALIST)
|
||||||
|
if (playerLevel >= 25) unlockedThemes.add(THEME_GALAXY)
|
||||||
|
|
||||||
|
// Check block skin unlocks
|
||||||
|
for (i in 1..5) {
|
||||||
|
val requiredLevel = i * 7
|
||||||
|
if (playerLevel >= requiredLevel) {
|
||||||
|
unlockedBlocks.add("block_skin_$i")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save any newly unlocked items
|
||||||
|
saveProgress()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a new progression session
|
* Start a new progression session
|
||||||
*/
|
*/
|
||||||
fun startNewSession() {
|
fun startNewSession() {
|
||||||
sessionXPGained = 0
|
sessionXPGained = 0
|
||||||
|
|
||||||
|
// Ensure all appropriate unlocks are available
|
||||||
|
checkAllUnlocksForCurrentLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
@ -227,7 +231,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
fun getXPForNextLevel(): Long = calculateXPForLevel(playerLevel)
|
fun getXPForNextLevel(): Long = calculateXPForLevel(playerLevel)
|
||||||
fun getSessionXPGained(): Long = sessionXPGained
|
fun getSessionXPGained(): Long = sessionXPGained
|
||||||
fun getUnlockedThemes(): Set<String> = unlockedThemes.toSet()
|
fun getUnlockedThemes(): Set<String> = unlockedThemes.toSet()
|
||||||
fun getUnlockedPowers(): Set<String> = unlockedPowers.toSet()
|
|
||||||
fun getUnlockedBlocks(): Set<String> = unlockedBlocks.toSet()
|
fun getUnlockedBlocks(): Set<String> = unlockedBlocks.toSet()
|
||||||
fun getUnlockedBadges(): Set<String> = unlockedBadges.toSet()
|
fun getUnlockedBadges(): Set<String> = unlockedBadges.toSet()
|
||||||
|
|
||||||
|
@ -238,13 +241,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
return unlockedThemes.contains(themeId)
|
return unlockedThemes.contains(themeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a specific power is unlocked
|
|
||||||
*/
|
|
||||||
fun isPowerUnlocked(powerId: String): Boolean {
|
|
||||||
return unlockedPowers.contains(powerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Award a badge to the player
|
* Award a badge to the player
|
||||||
*/
|
*/
|
||||||
|
@ -266,12 +262,14 @@ class PlayerProgressionManager(context: Context) {
|
||||||
|
|
||||||
unlockedThemes.clear()
|
unlockedThemes.clear()
|
||||||
unlockedBlocks.clear()
|
unlockedBlocks.clear()
|
||||||
unlockedPowers.clear()
|
|
||||||
unlockedBadges.clear()
|
unlockedBadges.clear()
|
||||||
|
|
||||||
// Add default theme
|
// Add default theme
|
||||||
unlockedThemes.add(THEME_CLASSIC)
|
unlockedThemes.add(THEME_CLASSIC)
|
||||||
|
|
||||||
|
// Add default block skin
|
||||||
|
unlockedBlocks.add("block_skin_1")
|
||||||
|
|
||||||
saveProgress()
|
saveProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,8 +280,9 @@ class PlayerProgressionManager(context: Context) {
|
||||||
private const val KEY_TOTAL_XP_EARNED = "total_xp_earned"
|
private const val KEY_TOTAL_XP_EARNED = "total_xp_earned"
|
||||||
private const val KEY_UNLOCKED_THEMES = "unlocked_themes"
|
private const val KEY_UNLOCKED_THEMES = "unlocked_themes"
|
||||||
private const val KEY_UNLOCKED_BLOCKS = "unlocked_blocks"
|
private const val KEY_UNLOCKED_BLOCKS = "unlocked_blocks"
|
||||||
private const val KEY_UNLOCKED_POWERS = "unlocked_powers"
|
|
||||||
private const val KEY_UNLOCKED_BADGES = "unlocked_badges"
|
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"
|
||||||
|
|
||||||
// XP curve parameters
|
// XP curve parameters
|
||||||
private const val BASE_XP = 4000.0 // Base XP for level 1 (reduced from 5000)
|
private const val BASE_XP = 4000.0 // Base XP for level 1 (reduced from 5000)
|
||||||
|
@ -313,20 +312,6 @@ class PlayerProgressionManager(context: Context) {
|
||||||
THEME_MINIMALIST to 20,
|
THEME_MINIMALIST to 20,
|
||||||
THEME_GALAXY to 25
|
THEME_GALAXY to 25
|
||||||
)
|
)
|
||||||
|
|
||||||
// Power IDs
|
|
||||||
const val POWER_FREEZE_TIME = "power_freeze_time"
|
|
||||||
const val POWER_BLOCK_SWAP = "power_block_swap"
|
|
||||||
const val POWER_SAFE_LANDING = "power_safe_landing"
|
|
||||||
const val POWER_PERFECT_CLEAR = "power_perfect_clear"
|
|
||||||
|
|
||||||
// Map of powers to required levels
|
|
||||||
val POWER_REQUIRED_LEVELS = mapOf(
|
|
||||||
POWER_FREEZE_TIME to 8,
|
|
||||||
POWER_BLOCK_SWAP to 12,
|
|
||||||
POWER_SAFE_LANDING to 18,
|
|
||||||
POWER_PERFECT_CLEAR to 30
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,9 +322,34 @@ class PlayerProgressionManager(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the required level for a specific power
|
* Set the selected block skin
|
||||||
*/
|
*/
|
||||||
fun getRequiredLevelForPower(powerId: String): Int {
|
fun setSelectedBlockSkin(skinId: String) {
|
||||||
return POWER_REQUIRED_LEVELS[powerId] ?: 1
|
if (unlockedBlocks.contains(skinId)) {
|
||||||
|
prefs.edit().putString(KEY_SELECTED_BLOCK_SKIN, skinId).apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected block skin
|
||||||
|
*/
|
||||||
|
fun getSelectedBlockSkin(): String {
|
||||||
|
return prefs.getString(KEY_SELECTED_BLOCK_SKIN, "block_skin_1") ?: "block_skin_1"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected theme
|
||||||
|
*/
|
||||||
|
fun setSelectedTheme(themeId: String) {
|
||||||
|
if (unlockedThemes.contains(themeId)) {
|
||||||
|
prefs.edit().putString(KEY_SELECTED_THEME, themeId).apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected theme
|
||||||
|
*/
|
||||||
|
fun getSelectedTheme(): String {
|
||||||
|
return prefs.getString(KEY_SELECTED_THEME, THEME_CLASSIC) ?: THEME_CLASSIC
|
||||||
}
|
}
|
||||||
}
|
}
|
288
app/src/main/java/com/mintris/ui/BlockSkinSelector.kt
Normal file
288
app/src/main/java/com/mintris/ui/BlockSkinSelector.kt
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
package com.mintris.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.GridLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
|
import com.mintris.R
|
||||||
|
import com.mintris.model.PlayerProgressionManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI component for selecting block skins
|
||||||
|
*/
|
||||||
|
class BlockSkinSelector @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val skinsGrid: GridLayout
|
||||||
|
private val availableSkinsLabel: TextView
|
||||||
|
|
||||||
|
// Callback when a block skin is selected
|
||||||
|
var onBlockSkinSelected: ((String) -> Unit)? = null
|
||||||
|
|
||||||
|
// Currently selected block skin
|
||||||
|
private var selectedSkin: String = "block_skin_1"
|
||||||
|
|
||||||
|
// Block skin cards
|
||||||
|
private val skinCards = mutableMapOf<String, CardView>()
|
||||||
|
|
||||||
|
// Player level for determining what should be unlocked
|
||||||
|
private var playerLevel: Int = 1
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Inflate the layout
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.block_skin_selector, this, true)
|
||||||
|
|
||||||
|
// Get references to views
|
||||||
|
skinsGrid = findViewById(R.id.skins_grid)
|
||||||
|
availableSkinsLabel = findViewById(R.id.available_skins_label)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the block skin selector with unlocked skins
|
||||||
|
*/
|
||||||
|
fun updateBlockSkins(unlockedSkins: Set<String>, currentSkin: String, playerLevel: Int = 1) {
|
||||||
|
// Store player level
|
||||||
|
this.playerLevel = playerLevel
|
||||||
|
|
||||||
|
// Clear existing skin cards
|
||||||
|
skinsGrid.removeAllViews()
|
||||||
|
skinCards.clear()
|
||||||
|
|
||||||
|
// Update selected skin
|
||||||
|
selectedSkin = currentSkin
|
||||||
|
|
||||||
|
// Get all possible skins and their details
|
||||||
|
val allSkins = getBlockSkins()
|
||||||
|
|
||||||
|
// Add skin cards to the grid
|
||||||
|
allSkins.forEach { (skinId, skinInfo) ->
|
||||||
|
val isUnlocked = unlockedSkins.contains(skinId) || playerLevel >= skinInfo.unlockLevel
|
||||||
|
val isSelected = skinId == selectedSkin
|
||||||
|
|
||||||
|
val skinCard = createBlockSkinCard(skinId, skinInfo, isUnlocked, isSelected)
|
||||||
|
skinCards[skinId] = skinCard
|
||||||
|
skinsGrid.addView(skinCard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a card for a block skin
|
||||||
|
*/
|
||||||
|
private fun createBlockSkinCard(
|
||||||
|
skinId: String,
|
||||||
|
skinInfo: BlockSkinInfo,
|
||||||
|
isUnlocked: Boolean,
|
||||||
|
isSelected: Boolean
|
||||||
|
): CardView {
|
||||||
|
// Create the card
|
||||||
|
val card = CardView(context).apply {
|
||||||
|
id = View.generateViewId()
|
||||||
|
radius = 12f
|
||||||
|
cardElevation = if (isSelected) 8f else 2f
|
||||||
|
useCompatPadding = true
|
||||||
|
|
||||||
|
// Set card background color based on skin
|
||||||
|
setCardBackgroundColor(skinInfo.backgroundColor)
|
||||||
|
|
||||||
|
// Add more noticeable visual indicator for selected skin
|
||||||
|
if (isSelected) {
|
||||||
|
setContentPadding(4, 4, 4, 4)
|
||||||
|
// Create a gradient drawable for the border
|
||||||
|
val gradientDrawable = android.graphics.drawable.GradientDrawable().apply {
|
||||||
|
setColor(skinInfo.backgroundColor)
|
||||||
|
setStroke(6, Color.WHITE) // Thicker border
|
||||||
|
cornerRadius = 12f
|
||||||
|
}
|
||||||
|
background = gradientDrawable
|
||||||
|
// Add glow effect via elevation
|
||||||
|
cardElevation = 12f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set card dimensions
|
||||||
|
val cardSize = resources.getDimensionPixelSize(R.dimen.theme_card_size)
|
||||||
|
layoutParams = GridLayout.LayoutParams().apply {
|
||||||
|
width = cardSize
|
||||||
|
height = cardSize
|
||||||
|
columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
|
||||||
|
rowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
|
||||||
|
setMargins(8, 8, 8, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply locked/selected state visuals
|
||||||
|
alpha = if (isUnlocked) 1.0f else 0.5f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create block skin content container
|
||||||
|
val container = FrameLayout(context).apply {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the block skin preview
|
||||||
|
val blockSkinPreview = View(context).apply {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set the background color
|
||||||
|
setBackgroundColor(skinInfo.backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a label with the skin name
|
||||||
|
val skinLabel = TextView(context).apply {
|
||||||
|
text = skinInfo.displayName
|
||||||
|
setTextColor(skinInfo.textColor)
|
||||||
|
textSize = 14f
|
||||||
|
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||||
|
|
||||||
|
// Position at the bottom of the card
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply {
|
||||||
|
gravity = android.view.Gravity.BOTTOM or android.view.Gravity.CENTER_HORIZONTAL
|
||||||
|
setMargins(4, 4, 4, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add level requirement for locked skins
|
||||||
|
val levelRequirement = TextView(context).apply {
|
||||||
|
text = "Level ${skinInfo.unlockLevel}"
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 12f
|
||||||
|
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||||
|
visibility = if (isUnlocked) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
|
// Position at the center of the card
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply {
|
||||||
|
gravity = android.view.Gravity.CENTER
|
||||||
|
}
|
||||||
|
// Make text bold and more visible for better readability
|
||||||
|
typeface = android.graphics.Typeface.DEFAULT_BOLD
|
||||||
|
setShadowLayer(3f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a lock icon if the skin is locked
|
||||||
|
val lockOverlay = View(context).apply {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add lock icon or visual indicator
|
||||||
|
setBackgroundResource(R.drawable.lock_overlay)
|
||||||
|
visibility = if (isUnlocked) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all elements to container
|
||||||
|
container.addView(blockSkinPreview)
|
||||||
|
container.addView(skinLabel)
|
||||||
|
container.addView(lockOverlay)
|
||||||
|
container.addView(levelRequirement)
|
||||||
|
|
||||||
|
// Add container to card
|
||||||
|
card.addView(container)
|
||||||
|
|
||||||
|
// Set up click listener only for unlocked skins
|
||||||
|
if (isUnlocked) {
|
||||||
|
card.setOnClickListener {
|
||||||
|
// Only trigger callback if this isn't already the selected skin
|
||||||
|
if (skinId != selectedSkin) {
|
||||||
|
// Update previously selected card
|
||||||
|
skinCards[selectedSkin]?.let { prevCard ->
|
||||||
|
prevCard.cardElevation = 2f
|
||||||
|
// Reset any special styling
|
||||||
|
prevCard.background = null
|
||||||
|
prevCard.setCardBackgroundColor(getBlockSkins()[selectedSkin]?.backgroundColor ?: Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update visual state of newly selected card
|
||||||
|
card.cardElevation = 12f
|
||||||
|
|
||||||
|
// Flash animation for selection feedback
|
||||||
|
val flashColor = Color.WHITE
|
||||||
|
val originalColor = skinInfo.backgroundColor
|
||||||
|
|
||||||
|
// Create animator for flash effect
|
||||||
|
val flashAnimator = android.animation.ValueAnimator.ofArgb(flashColor, originalColor)
|
||||||
|
flashAnimator.duration = 300 // 300ms
|
||||||
|
flashAnimator.addUpdateListener { animator ->
|
||||||
|
val color = animator.animatedValue as Int
|
||||||
|
card.setCardBackgroundColor(color)
|
||||||
|
}
|
||||||
|
flashAnimator.start()
|
||||||
|
|
||||||
|
// Update selected skin
|
||||||
|
selectedSkin = skinId
|
||||||
|
|
||||||
|
// Notify listener
|
||||||
|
onBlockSkinSelected?.invoke(skinId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return card
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data class for block skin information
|
||||||
|
*/
|
||||||
|
data class BlockSkinInfo(
|
||||||
|
val displayName: String,
|
||||||
|
val backgroundColor: Int,
|
||||||
|
val textColor: Int,
|
||||||
|
val unlockLevel: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available block skins with their details
|
||||||
|
*/
|
||||||
|
private fun getBlockSkins(): Map<String, BlockSkinInfo> {
|
||||||
|
return mapOf(
|
||||||
|
"block_skin_1" to BlockSkinInfo(
|
||||||
|
displayName = "Classic",
|
||||||
|
backgroundColor = Color.BLACK,
|
||||||
|
textColor = Color.WHITE,
|
||||||
|
unlockLevel = 1
|
||||||
|
),
|
||||||
|
"block_skin_2" to BlockSkinInfo(
|
||||||
|
displayName = "Neon",
|
||||||
|
backgroundColor = Color.parseColor("#0D0221"),
|
||||||
|
textColor = Color.parseColor("#FF00FF"),
|
||||||
|
unlockLevel = 7
|
||||||
|
),
|
||||||
|
"block_skin_3" to BlockSkinInfo(
|
||||||
|
displayName = "Retro",
|
||||||
|
backgroundColor = Color.parseColor("#3F2832"),
|
||||||
|
textColor = Color.parseColor("#FF5A5F"),
|
||||||
|
unlockLevel = 14
|
||||||
|
),
|
||||||
|
"block_skin_4" to BlockSkinInfo(
|
||||||
|
displayName = "Minimalist",
|
||||||
|
backgroundColor = Color.WHITE,
|
||||||
|
textColor = Color.BLACK,
|
||||||
|
unlockLevel = 21
|
||||||
|
),
|
||||||
|
"block_skin_5" to BlockSkinInfo(
|
||||||
|
displayName = "Galaxy",
|
||||||
|
backgroundColor = Color.parseColor("#0B0C10"),
|
||||||
|
textColor = Color.parseColor("#66FCF1"),
|
||||||
|
unlockLevel = 28
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -401,6 +401,14 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:layout_marginBottom="16dp" />
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- Block Skin Selector -->
|
||||||
|
<com.mintris.ui.BlockSkinSelector
|
||||||
|
android:id="@+id/blockSkinSelector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/settingsButton"
|
android:id="@+id/settingsButton"
|
||||||
|
|
27
app/src/main/res/layout/block_skin_selector.xml
Normal file
27
app/src/main/res/layout/block_skin_selector.xml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/available_skins_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="BLOCK SKINS"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
android:id="@+id/skins_grid"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"
|
||||||
|
android:alignmentMode="alignMargins"
|
||||||
|
android:useDefaultMargins="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
Loading…
Add table
Add a link
Reference in a new issue