diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index 8b4602e..abfc24c 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -366,6 +366,12 @@ class MainActivity : AppCompatActivity() { // Flag to track if high score screen will be shown var showingHighScore = false + // Play game over sound and trigger animation + if (isSoundEnabled) { + gameMusic.playGameOver() + } + gameView.startGameOverAnimation() + // Show progression screen first with XP animation showProgressionScreen(xpGained, newRewards) diff --git a/app/src/main/java/com/mintris/audio/GameMusic.kt b/app/src/main/java/com/mintris/audio/GameMusic.kt index fbb8abf..32c2f6e 100644 --- a/app/src/main/java/com/mintris/audio/GameMusic.kt +++ b/app/src/main/java/com/mintris/audio/GameMusic.kt @@ -9,12 +9,14 @@ import com.mintris.R class GameMusic(private val context: Context) { private var mediaPlayer: MediaPlayer? = null + private var gameOverPlayer: MediaPlayer? = null private var isEnabled = true private var isPrepared = false init { try { setupMediaPlayer() + setupGameOverPlayer() } catch (e: Exception) { Log.e("GameMusic", "Error initializing: ${e.message}") } @@ -46,6 +48,49 @@ class GameMusic(private val context: Context) { } } + private fun setupGameOverPlayer() { + try { + Log.d("GameMusic", "Setting up GameOver MediaPlayer") + gameOverPlayer = MediaPlayer.create(context, R.raw.game_over).apply { + setVolume(1.0f, 1.0f) // Increased from 0.7f to 1.0f for maximum volume + + // Set audio attributes for better performance + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_GAME) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build() + ) + } + } + Log.d("GameMusic", "GameOver MediaPlayer setup complete") + } catch (e: Exception) { + Log.e("GameMusic", "Error setting up GameOver MediaPlayer", e) + gameOverPlayer = null + } + } + + fun playGameOver() { + if (isEnabled && gameOverPlayer != null) { + try { + Log.d("GameMusic", "Playing game over sound") + // Temporarily lower background music volume + mediaPlayer?.setVolume(0.2f, 0.2f) + + // Play game over sound + gameOverPlayer?.start() + + // Restore background music volume after a delay + gameOverPlayer?.setOnCompletionListener { + mediaPlayer?.setVolume(0.5f, 0.5f) + } + } catch (e: Exception) { + Log.e("GameMusic", "Error playing game over sound: ${e.message}") + } + } + } + fun start() { if (isEnabled && mediaPlayer != null && isPrepared) { try { @@ -107,7 +152,9 @@ class GameMusic(private val context: Context) { try { Log.d("GameMusic", "Releasing MediaPlayer") mediaPlayer?.release() + gameOverPlayer?.release() mediaPlayer = null + gameOverPlayer = null isPrepared = false } catch (e: Exception) { Log.e("GameMusic", "Error releasing music: ${e.message}") diff --git a/app/src/main/java/com/mintris/game/GameHaptics.kt b/app/src/main/java/com/mintris/game/GameHaptics.kt index 44e5011..d6869c0 100644 --- a/app/src/main/java/com/mintris/game/GameHaptics.kt +++ b/app/src/main/java/com/mintris/game/GameHaptics.kt @@ -91,4 +91,26 @@ class GameHaptics(private val context: Context) { vibrator.vibrate(vibrationEffect) } } + + fun vibrateForGameOver() { + Log.d(TAG, "Attempting to vibrate for game over") + + // Only proceed if the device has a vibrator and it's available + if (!vibrator.hasVibrator()) return + + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Create a strong, long vibration effect + val vibrationEffect = VibrationEffect.createOneShot(300L, VibrationEffect.DEFAULT_AMPLITUDE) + vibrator.vibrate(vibrationEffect) + Log.d(TAG, "Game over vibration triggered successfully") + } else { + @Suppress("DEPRECATION") + vibrator.vibrate(300L) + Log.w(TAG, "Device does not support vibration effects (Android < 8.0)") + } + } catch (e: Exception) { + Log.e(TAG, "Error triggering game over vibration", e) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index 27e38a0..85a44a4 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -178,6 +178,9 @@ class GameView @JvmOverloads constructor( private var pulseAlpha = 0f private var isPulsing = false private var linesToPulse = mutableListOf() // Track which lines are being cleared + private var gameOverAnimator: ValueAnimator? = null + private var gameOverAlpha = 0f + private var isGameOverAnimating = false private val ghostPaint = Paint().apply { style = Paint.Style.STROKE @@ -439,6 +442,18 @@ class GameView @JvmOverloads constructor( // Draw active piece drawActivePiece(canvas) } + + // Draw game over effect if animating + if (isGameOverAnimating) { + val gameOverPaint = Paint().apply { + color = Color.WHITE + alpha = (128 * gameOverAlpha).toInt() + isAntiAlias = true + style = Paint.Style.FILL + maskFilter = BlurMaskFilter(48f * gameOverAlpha, BlurMaskFilter.Blur.OUTER) + } + canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), gameOverPaint) + } } /** @@ -1133,4 +1148,41 @@ class GameView @JvmOverloads constructor( super.setBackgroundColor(color) invalidate() } + + /** + * Start the game over animation + */ + fun startGameOverAnimation() { + Log.d(TAG, "Starting game over animation") + + // Cancel any existing animations + pulseAnimator?.cancel() + gameOverAnimator?.cancel() + + // Trigger haptic feedback + gameHaptics?.vibrateForGameOver() + + // Create new game over animation + gameOverAnimator = ValueAnimator.ofFloat(0f, 1f, 0.7f).apply { + duration = 1500L // 1.5 seconds total + interpolator = LinearInterpolator() + + addUpdateListener { animation -> + gameOverAlpha = animation.animatedValue as Float + isGameOverAnimating = true + invalidate() + Log.d(TAG, "Game over animation update: alpha = $gameOverAlpha") + } + + addListener(object : android.animation.AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: android.animation.Animator) { + isGameOverAnimating = false + gameOverAlpha = 0.7f // Keep at 70% opacity + invalidate() + Log.d(TAG, "Game over animation ended") + } + }) + } + gameOverAnimator?.start() + } } diff --git a/app/src/main/java/com/mintris/model/GameBoard.kt b/app/src/main/java/com/mintris/model/GameBoard.kt index 8d3c700..31f03aa 100644 --- a/app/src/main/java/com/mintris/model/GameBoard.kt +++ b/app/src/main/java/com/mintris/model/GameBoard.kt @@ -136,10 +136,10 @@ class GameBoard( currentPiece = nextPiece spawnNextPiece() - // Center the piece horizontally + // Center the piece horizontally and spawn one unit higher currentPiece?.apply { x = (width - getWidth()) / 2 - y = 0 + y = -1 // Spawn one unit above the top of the screen Log.d(TAG, "spawnPiece() - new piece spawned at position (${x},${y}), type=${type}") @@ -326,6 +326,11 @@ class GameBoard( if (boardY >= 0 && grid[boardY][boardX]) { return false } + + // Check if the position is more than one unit above the top of the screen + if (boardY < -1) { + return false + } } } } diff --git a/app/src/main/res/raw/game_over.mp3 b/app/src/main/res/raw/game_over.mp3 new file mode 100644 index 0000000..d189281 Binary files /dev/null and b/app/src/main/res/raw/game_over.mp3 differ