mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-18 08:05: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.StatsManager
|
||||
import com.mintris.ui.ProgressionScreen
|
||||
import com.mintris.ui.ThemeSelector
|
||||
import com.mintris.ui.BlockSkinSelector
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import android.graphics.Color
|
||||
|
@ -45,6 +47,8 @@ class MainActivity : AppCompatActivity() {
|
|||
private lateinit var statsManager: StatsManager
|
||||
private lateinit var progressionManager: PlayerProgressionManager
|
||||
private lateinit var progressionScreen: ProgressionScreen
|
||||
private lateinit var themeSelector: ThemeSelector
|
||||
private lateinit var blockSkinSelector: BlockSkinSelector
|
||||
|
||||
// Game state
|
||||
private var isSoundEnabled = true
|
||||
|
@ -84,11 +88,16 @@ class MainActivity : AppCompatActivity() {
|
|||
highScoreManager = HighScoreManager(this)
|
||||
statsManager = StatsManager(this)
|
||||
progressionManager = PlayerProgressionManager(this)
|
||||
themeSelector = binding.themeSelector
|
||||
blockSkinSelector = binding.blockSkinSelector
|
||||
|
||||
// Load and apply theme preference
|
||||
currentTheme = loadThemePreference()
|
||||
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)
|
||||
|
@ -102,8 +111,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
// Set up theme selector
|
||||
val themeSelector = binding.themeSelector
|
||||
themeSelector.onThemeSelected = { themeId ->
|
||||
themeSelector.onThemeSelected = { themeId: String ->
|
||||
// Apply the new theme
|
||||
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
|
||||
titleScreen.onStartGame = {
|
||||
titleScreen.visibility = View.GONE
|
||||
|
@ -426,6 +446,13 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
// Update theme selector
|
||||
updateThemeSelector()
|
||||
|
||||
// Update block skin selector
|
||||
blockSkinSelector.updateBlockSkins(
|
||||
progressionManager.getUnlockedBlocks(),
|
||||
gameView.getCurrentBlockSkin(),
|
||||
progressionManager.getPlayerLevel()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -564,7 +591,7 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
// Save the selected theme
|
||||
currentTheme = themeId
|
||||
saveThemePreference(themeId)
|
||||
progressionManager.setSelectedTheme(themeId)
|
||||
|
||||
// Apply theme to title screen if it's visible
|
||||
if (titleScreen.visibility == View.VISIBLE) {
|
||||
|
@ -618,22 +645,6 @@ class MainActivity : AppCompatActivity() {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -144,6 +144,10 @@ class GameView @JvmOverloads constructor(
|
|||
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
|
||||
|
||||
// Block skin
|
||||
private var currentBlockSkin: String = "block_skin_1"
|
||||
private val blockSkinPaints = mutableMapOf<String, Paint>()
|
||||
|
||||
private enum class Direction {
|
||||
HORIZONTAL, VERTICAL
|
||||
}
|
||||
|
@ -206,8 +210,64 @@ class GameView @JvmOverloads constructor(
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
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
|
||||
*/
|
||||
|
@ -519,16 +579,19 @@ class GameView @JvmOverloads constructor(
|
|||
// Save canvas state before drawing block effects
|
||||
canvas.save()
|
||||
|
||||
// Get the current block skin paint
|
||||
val paint = blockSkinPaints[currentBlockSkin] ?: blockSkinPaints["block_skin_1"]!!
|
||||
|
||||
// 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.apply {
|
||||
color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
||||
// Draw block with current skin
|
||||
paint.apply {
|
||||
color = if (isGhost) Color.argb(30, 255, 255, 255) else paint.color
|
||||
alpha = if (isGhost) 30 else 255
|
||||
}
|
||||
canvas.drawRect(left, top, right, bottom, blockPaint)
|
||||
canvas.drawRect(left, top, right, bottom, paint)
|
||||
|
||||
// Draw inner glow
|
||||
glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
|
||||
|
|
|
@ -20,7 +20,6 @@ class PlayerProgressionManager(context: Context) {
|
|||
// Track unlocked rewards
|
||||
private val unlockedThemes = mutableSetOf<String>()
|
||||
private val unlockedBlocks = mutableSetOf<String>()
|
||||
private val unlockedPowers = mutableSetOf<String>()
|
||||
private val unlockedBadges = mutableSetOf<String>()
|
||||
|
||||
// XP gained in the current session
|
||||
|
@ -41,18 +40,21 @@ class PlayerProgressionManager(context: Context) {
|
|||
// Load unlocked rewards
|
||||
val themesSet = prefs.getStringSet(KEY_UNLOCKED_THEMES, 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()
|
||||
|
||||
unlockedThemes.addAll(themesSet)
|
||||
unlockedBlocks.addAll(blocksSet)
|
||||
unlockedPowers.addAll(powersSet)
|
||||
unlockedBadges.addAll(badgesSet)
|
||||
|
||||
// Add default theme if nothing is unlocked
|
||||
if (unlockedThemes.isEmpty()) {
|
||||
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)
|
||||
.putStringSet(KEY_UNLOCKED_THEMES, unlockedThemes)
|
||||
.putStringSet(KEY_UNLOCKED_BLOCKS, unlockedBlocks)
|
||||
.putStringSet(KEY_UNLOCKED_POWERS, unlockedPowers)
|
||||
.putStringSet(KEY_UNLOCKED_BADGES, unlockedBadges)
|
||||
.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
|
||||
if (level % 7 == 0 && level <= 35) {
|
||||
val blockSkin = "block_skin_${level / 7}"
|
||||
|
@ -214,11 +191,38 @@ class PlayerProgressionManager(context: Context) {
|
|||
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
|
||||
*/
|
||||
fun startNewSession() {
|
||||
sessionXPGained = 0
|
||||
|
||||
// Ensure all appropriate unlocks are available
|
||||
checkAllUnlocksForCurrentLevel()
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
@ -227,7 +231,6 @@ class PlayerProgressionManager(context: Context) {
|
|||
fun getXPForNextLevel(): Long = calculateXPForLevel(playerLevel)
|
||||
fun getSessionXPGained(): Long = sessionXPGained
|
||||
fun getUnlockedThemes(): Set<String> = unlockedThemes.toSet()
|
||||
fun getUnlockedPowers(): Set<String> = unlockedPowers.toSet()
|
||||
fun getUnlockedBlocks(): Set<String> = unlockedBlocks.toSet()
|
||||
fun getUnlockedBadges(): Set<String> = unlockedBadges.toSet()
|
||||
|
||||
|
@ -238,13 +241,6 @@ class PlayerProgressionManager(context: Context) {
|
|||
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
|
||||
*/
|
||||
|
@ -266,12 +262,14 @@ class PlayerProgressionManager(context: Context) {
|
|||
|
||||
unlockedThemes.clear()
|
||||
unlockedBlocks.clear()
|
||||
unlockedPowers.clear()
|
||||
unlockedBadges.clear()
|
||||
|
||||
// Add default theme
|
||||
unlockedThemes.add(THEME_CLASSIC)
|
||||
|
||||
// Add default block skin
|
||||
unlockedBlocks.add("block_skin_1")
|
||||
|
||||
saveProgress()
|
||||
}
|
||||
|
||||
|
@ -282,8 +280,9 @@ 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_POWERS = "unlocked_powers"
|
||||
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
|
||||
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_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 {
|
||||
return POWER_REQUIRED_LEVELS[powerId] ?: 1
|
||||
fun setSelectedBlockSkin(skinId: String) {
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -402,6 +402,14 @@
|
|||
android:layout_marginTop="24dp"
|
||||
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
|
||||
android:id="@+id/settingsButton"
|
||||
android:layout_width="200dp"
|
||||
|
|
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