diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index 5649f9f..c535adf 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -269,6 +269,15 @@ class MainActivity : AppCompatActivity(), updateUI(score, level, lines) } + // Track pieces placed using callback + gameBoard.onPieceLock = { + // Increment pieces placed counter + piecesPlaced++ + + // Provide haptic feedback + gameHaptics.vibrateForPieceLock() + } + gameView.onGameOver = { finalScore -> // Pause music on game over gameMusic.pause() @@ -325,15 +334,6 @@ class MainActivity : AppCompatActivity(), } } - // Track pieces placed using callback - gameView.onPieceLock = { - // Increment pieces placed counter - piecesPlaced++ - - // Provide haptic feedback - gameHaptics.vibrateForPieceLock() - } - // Set up button click listeners with haptic feedback binding.playAgainButton.setOnClickListener { gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY) @@ -484,6 +484,12 @@ class MainActivity : AppCompatActivity(), Log.d("MainActivity", "Triggering game over animation") gameView.startGameOverAnimation() + // Hide game UI elements in landscape mode + if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) { + binding.leftControlsPanel?.visibility = View.GONE + binding.rightControlsPanel?.visibility = View.GONE + } + // Wait a moment before showing progression screen to let animation be visible Handler(Looper.getMainLooper()).postDelayed({ // Show progression screen first with XP animation @@ -660,10 +666,12 @@ class MainActivity : AppCompatActivity(), * Start a new game */ private fun startGame() { + // Reset pieces placed counter + piecesPlaced = 0 + // Set initial game state currentScore = 0 currentLevel = selectedLevel - piecesPlaced = 0 gameStartTime = System.currentTimeMillis() // Update UI to show initial values @@ -673,8 +681,23 @@ class MainActivity : AppCompatActivity(), binding.comboText.text = "0" // Reset game view and game board + gameBoard.reset() gameView.reset() + // Show game elements + gameView.visibility = View.VISIBLE + binding.gameControlsContainer.visibility = View.VISIBLE + binding.gameOverContainer.visibility = View.GONE + binding.pauseContainer.visibility = View.GONE + titleScreen.visibility = View.GONE + progressionScreen.visibility = View.GONE + + // Show game UI elements in landscape mode + if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) { + binding.leftControlsPanel?.visibility = View.VISIBLE + binding.rightControlsPanel?.visibility = View.VISIBLE + } + // Configure callbacks gameView.onGameStateChanged = { score, level, lines -> currentScore = score @@ -729,15 +752,19 @@ class MainActivity : AppCompatActivity(), statsManager.recordLineClear(lineCount) } - // Start the game - gameView.start() - gameMusic.setEnabled(isMusicEnabled) + // Reset pause button state + binding.pauseStartButton.visibility = View.VISIBLE + binding.resumeButton.visibility = View.GONE // Start background music if enabled if (isMusicEnabled) { gameMusic.start() } + // Start the game + gameView.start() + gameMusic.setEnabled(isMusicEnabled) + // Reset session stats statsManager.startNewSession() progressionManager.startNewSession() @@ -957,6 +984,8 @@ class MainActivity : AppCompatActivity(), override fun onGamepadConnected(gamepadName: String) { runOnUiThread { Toast.makeText(this, "Gamepad connected: $gamepadName", Toast.LENGTH_SHORT).show() + // Set gamepad connected state in haptics + gameHaptics.setGamepadConnected(true) // Provide haptic feedback for gamepad connection gameHaptics.vibrateForPieceLock() @@ -970,6 +999,8 @@ class MainActivity : AppCompatActivity(), override fun onGamepadDisconnected(gamepadName: String) { runOnUiThread { Toast.makeText(this, "Gamepad disconnected: $gamepadName", Toast.LENGTH_SHORT).show() + // Set gamepad disconnected state in haptics + gameHaptics.setGamepadConnected(false) } } diff --git a/app/src/main/java/com/mintris/game/GameHaptics.kt b/app/src/main/java/com/mintris/game/GameHaptics.kt index d6869c0..731ac8b 100644 --- a/app/src/main/java/com/mintris/game/GameHaptics.kt +++ b/app/src/main/java/com/mintris/game/GameHaptics.kt @@ -27,6 +27,19 @@ class GameHaptics(private val context: Context) { context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator } + // Track if gamepad is connected + private var isGamepadConnected = false + + // Set gamepad connection state + fun setGamepadConnected(connected: Boolean) { + isGamepadConnected = connected + } + + // Get vibration multiplier based on input method + private fun getVibrationMultiplier(): Float { + return if (isGamepadConnected) 1.5f else 1.0f + } + // Vibrate for line clear (more intense for more lines) fun vibrateForLineClear(lineCount: Int) { Log.d(TAG, "Attempting to vibrate for $lineCount lines") @@ -34,22 +47,22 @@ class GameHaptics(private val context: Context) { // Only proceed if the device has a vibrator and it's available if (!vibrator.hasVibrator()) return - // Scale duration and amplitude based on line count - // More lines = longer and stronger vibration + // Scale duration and amplitude based on line count and input method + val multiplier = getVibrationMultiplier() val duration = when(lineCount) { - 1 -> 50L // Single line: short vibration - 2 -> 80L // Double line: slightly longer - 3 -> 120L // Triple line: even longer - 4 -> 200L // Tetris: longest vibration - else -> 50L + 1 -> (50L * multiplier).toLong() // Single line: short vibration + 2 -> (80L * multiplier).toLong() // Double line: slightly longer + 3 -> (120L * multiplier).toLong() // Triple line: even longer + 4 -> (200L * multiplier).toLong() // Tetris: longest vibration + else -> (50L * multiplier).toLong() } val amplitude = when(lineCount) { - 1 -> 80 // Single line: mild vibration (80/255) - 2 -> 120 // Double line: medium vibration (120/255) - 3 -> 180 // Triple line: strong vibration (180/255) - 4 -> 255 // Tetris: maximum vibration (255/255) - else -> 80 + 1 -> (80 * multiplier).toInt().coerceAtMost(255) // Single line: mild vibration + 2 -> (120 * multiplier).toInt().coerceAtMost(255) // Double line: medium vibration + 3 -> (180 * multiplier).toInt().coerceAtMost(255) // Triple line: strong vibration + 4 -> 255 // Tetris: maximum vibration + else -> (80 * multiplier).toInt().coerceAtMost(255) } Log.d(TAG, "Vibration parameters - Duration: ${duration}ms, Amplitude: $amplitude") @@ -79,15 +92,20 @@ class GameHaptics(private val context: Context) { fun vibrateForPieceLock() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val vibrationEffect = VibrationEffect.createOneShot(50L, VibrationEffect.DEFAULT_AMPLITUDE) + val multiplier = getVibrationMultiplier() + val duration = (50L * multiplier).toLong() + val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * multiplier).toInt().coerceAtMost(255) + val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude) vibrator.vibrate(vibrationEffect) } } fun vibrateForPieceMove() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * 0.3).toInt().coerceAtLeast(1) - val vibrationEffect = VibrationEffect.createOneShot(20L, amplitude) + val multiplier = getVibrationMultiplier() + val duration = (20L * multiplier).toLong() + val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * 0.3 * multiplier).toInt().coerceAtLeast(1).coerceAtMost(255) + val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude) vibrator.vibrate(vibrationEffect) } } @@ -100,8 +118,10 @@ class GameHaptics(private val context: Context) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Create a strong, long vibration effect - val vibrationEffect = VibrationEffect.createOneShot(300L, VibrationEffect.DEFAULT_AMPLITUDE) + val multiplier = getVibrationMultiplier() + val duration = (300L * multiplier).toLong() + val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * multiplier).toInt().coerceAtMost(255) + val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude) vibrator.vibrate(vibrationEffect) Log.d(TAG, "Game over vibration triggered successfully") } else { diff --git a/app/src/main/java/com/mintris/game/TitleScreen.kt b/app/src/main/java/com/mintris/game/TitleScreen.kt index 34627f7..324f81b 100644 --- a/app/src/main/java/com/mintris/game/TitleScreen.kt +++ b/app/src/main/java/com/mintris/game/TitleScreen.kt @@ -245,6 +245,7 @@ class TitleScreen @JvmOverloads constructor( // Draw high scores using pre-allocated manager val highScores: List = highScoreManager.getHighScores() val highScoreY = height * 0.5f + var lastHighScoreY = highScoreY if (highScores.isNotEmpty()) { // Calculate the starting X position to center the entire block of scores val maxScoreWidth = highScorePaint.measureText("99. PLAYER: 999999") @@ -252,6 +253,7 @@ class TitleScreen @JvmOverloads constructor( highScores.forEachIndexed { index: Int, score: HighScore -> val y = highScoreY + (index * 80f) + lastHighScoreY = y // Track the last high score's Y position // Pad the rank number to ensure alignment val rank = (index + 1).toString().padStart(2, ' ') // Pad the name to ensure score alignment @@ -260,8 +262,15 @@ class TitleScreen @JvmOverloads constructor( } } - // Draw "touch to start" prompt - canvas.drawText("touch to start", width / 2f, height * 0.7f, promptPaint) + // Draw "touch to start" prompt below the high scores + val promptY = if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) { + // In landscape mode, position below the last high score with some padding + lastHighScoreY + 100f + } else { + // In portrait mode, use the original position + height * 0.7f + } + canvas.drawText("touch to start", width / 2f, promptY, promptPaint) // Request another frame invalidate()