mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-17 23:45:22 +01:00
Implement full gamepad menu support and title screen start
This commit is contained in:
parent
779fa8eab1
commit
f4f40c4c34
2 changed files with 155 additions and 3 deletions
|
@ -41,7 +41,8 @@ import android.app.AlertDialog
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(),
|
class MainActivity : AppCompatActivity(),
|
||||||
GamepadController.GamepadConnectionListener,
|
GamepadController.GamepadConnectionListener,
|
||||||
GamepadController.GamepadMenuListener {
|
GamepadController.GamepadMenuListener,
|
||||||
|
GamepadController.GamepadNavigationListener {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MainActivity"
|
private const val TAG = "MainActivity"
|
||||||
|
@ -93,6 +94,10 @@ class MainActivity : AppCompatActivity(),
|
||||||
// Track connected gamepads to detect changes
|
// Track connected gamepads to detect changes
|
||||||
private val connectedGamepads = mutableSetOf<String>()
|
private val connectedGamepads = mutableSetOf<String>()
|
||||||
|
|
||||||
|
// Track currently selected menu item in pause menu for gamepad navigation
|
||||||
|
private var currentMenuSelection = 0
|
||||||
|
private val pauseMenuItems = mutableListOf<View>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// Register activity result launcher for high score entry
|
// Register activity result launcher for high score entry
|
||||||
highScoreEntryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
highScoreEntryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
@ -124,6 +129,7 @@ class MainActivity : AppCompatActivity(),
|
||||||
gamepadController = GamepadController(gameView)
|
gamepadController = GamepadController(gameView)
|
||||||
gamepadController.setGamepadConnectionListener(this)
|
gamepadController.setGamepadConnectionListener(this)
|
||||||
gamepadController.setGamepadMenuListener(this)
|
gamepadController.setGamepadMenuListener(this)
|
||||||
|
gamepadController.setGamepadNavigationListener(this)
|
||||||
|
|
||||||
// Set up touch event forwarding
|
// Set up touch event forwarding
|
||||||
binding.touchInterceptor?.setOnTouchListener { _, event ->
|
binding.touchInterceptor?.setOnTouchListener { _, event ->
|
||||||
|
@ -395,6 +401,9 @@ class MainActivity : AppCompatActivity(),
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
window.setDecorFitsSystemWindows(false)
|
window.setDecorFitsSystemWindows(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize pause menu items for gamepad navigation
|
||||||
|
initPauseMenuNavigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -576,7 +585,7 @@ class MainActivity : AppCompatActivity(),
|
||||||
unlockedThemes = progressionManager.getUnlockedThemes(),
|
unlockedThemes = progressionManager.getUnlockedThemes(),
|
||||||
currentTheme = currentTheme
|
currentTheme = currentTheme
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update block skin selector - handle both standard and landscape versions
|
// Update block skin selector - handle both standard and landscape versions
|
||||||
blockSkinSelector.updateBlockSkins(
|
blockSkinSelector.updateBlockSkins(
|
||||||
progressionManager.getUnlockedBlocks(),
|
progressionManager.getUnlockedBlocks(),
|
||||||
|
@ -591,6 +600,15 @@ class MainActivity : AppCompatActivity(),
|
||||||
gameView.getCurrentBlockSkin(),
|
gameView.getCurrentBlockSkin(),
|
||||||
progressionManager.getPlayerLevel()
|
progressionManager.getPlayerLevel()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Initialize pause menu navigation
|
||||||
|
initPauseMenuNavigation()
|
||||||
|
|
||||||
|
// Reset and highlight first selectable menu item
|
||||||
|
if (pauseMenuItems.isNotEmpty()) {
|
||||||
|
currentMenuSelection = if (binding.resumeButton.visibility == View.VISIBLE) 0 else 1
|
||||||
|
highlightMenuItem(currentMenuSelection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1013,6 +1031,13 @@ class MainActivity : AppCompatActivity(),
|
||||||
|
|
||||||
// First check if it's a gamepad input
|
// First check if it's a gamepad input
|
||||||
if (GamepadController.isGamepad(event.device)) {
|
if (GamepadController.isGamepad(event.device)) {
|
||||||
|
// Handle title screen start with gamepad
|
||||||
|
if (titleScreen.visibility == View.VISIBLE &&
|
||||||
|
(keyCode == KeyEvent.KEYCODE_BUTTON_A || keyCode == KeyEvent.KEYCODE_BUTTON_START)) {
|
||||||
|
titleScreen.performClick()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// If gamepad input was handled by the controller, consume the event
|
// If gamepad input was handled by the controller, consume the event
|
||||||
if (gamepadController.handleKeyEvent(keyCode, event)) {
|
if (gamepadController.handleKeyEvent(keyCode, event)) {
|
||||||
return true
|
return true
|
||||||
|
@ -1086,7 +1111,93 @@ class MainActivity : AppCompatActivity(),
|
||||||
// If pause menu is showing, handle as a resume
|
// If pause menu is showing, handle as a resume
|
||||||
hidePauseMenu()
|
hidePauseMenu()
|
||||||
resumeGame()
|
resumeGame()
|
||||||
|
} else if (titleScreen.visibility == View.VISIBLE) {
|
||||||
|
// If title screen is showing, start the game
|
||||||
|
titleScreen.performClick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements GamepadNavigationListener to handle menu navigation
|
||||||
|
*/
|
||||||
|
override fun onMenuUp() {
|
||||||
|
runOnUiThread {
|
||||||
|
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||||
|
moveMenuSelectionUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuDown() {
|
||||||
|
runOnUiThread {
|
||||||
|
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||||
|
moveMenuSelectionDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuSelect() {
|
||||||
|
runOnUiThread {
|
||||||
|
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||||
|
activateSelectedMenuItem()
|
||||||
|
} else if (titleScreen.visibility == View.VISIBLE) {
|
||||||
|
titleScreen.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize pause menu items for gamepad navigation
|
||||||
|
*/
|
||||||
|
private fun initPauseMenuNavigation() {
|
||||||
|
pauseMenuItems.clear()
|
||||||
|
pauseMenuItems.add(binding.resumeButton)
|
||||||
|
pauseMenuItems.add(binding.pauseStartButton)
|
||||||
|
pauseMenuItems.add(binding.pauseRestartButton)
|
||||||
|
pauseMenuItems.add(binding.highScoresButton)
|
||||||
|
pauseMenuItems.add(binding.statsButton)
|
||||||
|
pauseMenuItems.add(binding.pauseLevelUpButton)
|
||||||
|
pauseMenuItems.add(binding.pauseLevelDownButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight the currently selected menu item
|
||||||
|
*/
|
||||||
|
private fun highlightMenuItem(index: Int) {
|
||||||
|
// Reset all items to normal state
|
||||||
|
pauseMenuItems.forEachIndexed { i, item ->
|
||||||
|
item.alpha = if (i == index) 1.0f else 0.7f
|
||||||
|
item.scaleX = if (i == index) 1.2f else 1.0f
|
||||||
|
item.scaleY = if (i == index) 1.2f else 1.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move menu selection up
|
||||||
|
*/
|
||||||
|
private fun moveMenuSelectionUp() {
|
||||||
|
if (pauseMenuItems.isEmpty()) return
|
||||||
|
currentMenuSelection = (currentMenuSelection - 1 + pauseMenuItems.size) % pauseMenuItems.size
|
||||||
|
highlightMenuItem(currentMenuSelection)
|
||||||
|
gameHaptics.vibrateForPieceMove()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move menu selection down
|
||||||
|
*/
|
||||||
|
private fun moveMenuSelectionDown() {
|
||||||
|
if (pauseMenuItems.isEmpty()) return
|
||||||
|
currentMenuSelection = (currentMenuSelection + 1) % pauseMenuItems.size
|
||||||
|
highlightMenuItem(currentMenuSelection)
|
||||||
|
gameHaptics.vibrateForPieceMove()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate the currently selected menu item
|
||||||
|
*/
|
||||||
|
private fun activateSelectedMenuItem() {
|
||||||
|
if (pauseMenuItems.isEmpty() || currentMenuSelection < 0 || currentMenuSelection >= pauseMenuItems.size) return
|
||||||
|
pauseMenuItems[currentMenuSelection].performClick()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -149,9 +149,16 @@ class GamepadController(
|
||||||
fun onPauseRequested()
|
fun onPauseRequested()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GamepadNavigationListener {
|
||||||
|
fun onMenuUp()
|
||||||
|
fun onMenuDown()
|
||||||
|
fun onMenuSelect()
|
||||||
|
}
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
private var connectionListener: GamepadConnectionListener? = null
|
private var connectionListener: GamepadConnectionListener? = null
|
||||||
private var menuListener: GamepadMenuListener? = null
|
private var menuListener: GamepadMenuListener? = null
|
||||||
|
private var navigationListener: GamepadNavigationListener? = null
|
||||||
|
|
||||||
// Currently active gamepad for rumble
|
// Currently active gamepad for rumble
|
||||||
private var activeGamepad: InputDevice? = null
|
private var activeGamepad: InputDevice? = null
|
||||||
|
@ -170,6 +177,13 @@ class GamepadController(
|
||||||
menuListener = listener
|
menuListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a listener for gamepad navigation events
|
||||||
|
*/
|
||||||
|
fun setGamepadNavigationListener(listener: GamepadNavigationListener) {
|
||||||
|
navigationListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to check for newly connected gamepads.
|
* Function to check for newly connected gamepads.
|
||||||
* Call this periodically from the activity to detect connection changes.
|
* Call this periodically from the activity to detect connection changes.
|
||||||
|
@ -253,7 +267,34 @@ class GamepadController(
|
||||||
*/
|
*/
|
||||||
fun handleKeyEvent(keyCode: Int, event: KeyEvent): Boolean {
|
fun handleKeyEvent(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
// Skip if game is not active
|
// Skip if game is not active
|
||||||
if (!gameView.isActive()) return false
|
if (!gameView.isActive()) {
|
||||||
|
// Handle menu navigation even when game is not active
|
||||||
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||||
|
navigationListener?.onMenuUp()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||||
|
navigationListener?.onMenuDown()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A,
|
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||||
|
navigationListener?.onMenuSelect()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle menu/start button for pause menu
|
||||||
|
if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BUTTON_START) {
|
||||||
|
menuListener?.onPauseRequested()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val device = event.device
|
val device = event.device
|
||||||
if (!isGamepad(device)) return false
|
if (!isGamepad(device)) return false
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue