diff --git a/app/src/main/java/com/unogame/MainActivity.kt b/app/src/main/java/com/unogame/MainActivity.kt index 68ff3d1..93e0a16 100644 --- a/app/src/main/java/com/unogame/MainActivity.kt +++ b/app/src/main/java/com/unogame/MainActivity.kt @@ -354,13 +354,14 @@ fun UnoApp() { playerName = savedName, modeDisplayName = mode.displayName, mode = mode, - onStartGame = { totalPlayers, name -> + onStartGame = { totalPlayers, name, botNames -> if (name.isNotEmpty()) { savedName = name prefs.edit().putString("player_name", name).apply() } + val encoded = java.net.URLEncoder.encode(botNames.joinToString(","), "UTF-8") navController.navigate( - Screen.LocalGame.createRoute(modeName, totalPlayers, name) + Screen.LocalGame.createRoute(modeName, totalPlayers, name, encoded) ) }, onBack = { navController.popBackStack() } @@ -372,18 +373,23 @@ fun UnoApp() { arguments = listOf( navArgument("modeName") { type = NavType.StringType }, navArgument("totalPlayers") { type = NavType.IntType }, - navArgument("humanPlayerName") { type = NavType.StringType } + navArgument("humanPlayerName") { type = NavType.StringType }, + navArgument("botNames") { type = NavType.StringType } ) ) { backStackEntry -> val modeName = backStackEntry.arguments?.getString("modeName") ?: "NORMAL" val mode = try { GameMode.valueOf(modeName) } catch (_: Exception) { GameMode.NORMAL } val totalPlayers = backStackEntry.arguments?.getInt("totalPlayers") ?: 2 val humanPlayerName = backStackEntry.arguments?.getString("humanPlayerName") ?: "玩家" + val botNames = java.net.URLDecoder.decode( + backStackEntry.arguments?.getString("botNames") ?: "", "UTF-8" + ).split(",").filter { it.isNotEmpty() } LocalGameScreen( mode = mode, totalPlayers = totalPlayers, humanPlayerName = humanPlayerName, + botNames = botNames, onBackToMenu = { navController.navigate(Screen.MainMenu.route) { popUpTo(0) { inclusive = true } diff --git a/app/src/main/java/com/unogame/ui/navigation/Screen.kt b/app/src/main/java/com/unogame/ui/navigation/Screen.kt index fca5fb2..7cf3695 100644 --- a/app/src/main/java/com/unogame/ui/navigation/Screen.kt +++ b/app/src/main/java/com/unogame/ui/navigation/Screen.kt @@ -10,9 +10,9 @@ sealed class Screen(val route: String) { data object LocalSetup : Screen("local_setup/{modeName}") { fun createRoute(mode: String) = "local_setup/$mode" } - data object LocalGame : Screen("local_game/{modeName}/{totalPlayers}/{humanPlayerName}") { - fun createRoute(mode: String, totalPlayers: Int, humanPlayerName: String) = - "local_game/$mode/$totalPlayers/$humanPlayerName" + data object LocalGame : Screen("local_game/{modeName}/{totalPlayers}/{humanPlayerName}/{botNames}") { + fun createRoute(mode: String, totalPlayers: Int, humanPlayerName: String, botNames: String) = + "local_game/$mode/$totalPlayers/$humanPlayerName/$botNames" } data object Scoreboard : Screen("scoreboard") data object Rules : Screen("rules") diff --git a/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt b/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt index 593e05e..7a18a90 100644 --- a/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt @@ -109,6 +109,7 @@ fun LocalGameScreen( totalPlayers: Int, humanPlayerName: String, mode: GameMode, + botNames: List = emptyList(), onBackToMenu: () -> Unit ) { val scope = rememberCoroutineScope() @@ -119,11 +120,11 @@ fun LocalGameScreen( val gameStartTime = remember { System.currentTimeMillis() } val players = remember { - val botNames = pickUniqueBotNames(totalPlayers - 1) + val names = if (botNames.isEmpty()) pickUniqueBotNames(totalPlayers - 1) else botNames val list = mutableListOf() list.add(Player(id = "human", name = humanPlayerName, isCurrentTurn = true)) for (i in 0 until totalPlayers - 1) { - list.add(Player(id = "bot_${i + 2}", name = botNames[i], isCurrentTurn = false)) + list.add(Player(id = "bot_${i + 2}", name = names.getOrElse(i) { "机器人${i + 1}" }, isCurrentTurn = false)) } list } diff --git a/app/src/main/java/com/unogame/ui/screens/LocalSetupScreen.kt b/app/src/main/java/com/unogame/ui/screens/LocalSetupScreen.kt index df164ee..fcf46d4 100644 --- a/app/src/main/java/com/unogame/ui/screens/LocalSetupScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/LocalSetupScreen.kt @@ -29,7 +29,7 @@ fun LocalSetupScreen( playerName: String, modeDisplayName: String, mode: com.unogame.game.GameMode, - onStartGame: (Int, String) -> Unit, + onStartGame: (Int, String, List) -> Unit, onBack: () -> Unit ) { var totalPlayers by remember { mutableIntStateOf(2) } @@ -39,7 +39,7 @@ fun LocalSetupScreen( val initMaxHandSize = remember { com.unogame.game.GameRules.loadMaxHandSize(context, mode) } var maxHandSize by remember { mutableIntStateOf(initMaxHandSize) } - val botNames = remember { pickUniqueBotNames(3) } + var botNames by remember { mutableStateOf(pickUniqueBotNames(3).toMutableList()) } Box( modifier = Modifier @@ -241,12 +241,63 @@ fun LocalSetupScreen( // Bots for (i in 1 until totalPlayers) { + val botIdx = i - 1 + var botDropdownExpanded by remember { mutableStateOf(false) } Spacer(modifier = Modifier.height(8.dp)) - PlayerSlot( - name = botNames[i - 1], - isHuman = false, - isYou = false - ) + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors(containerColor = DarkSurface) + ) { + Row( + modifier = Modifier.padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(40.dp) + .clip(CircleShape) + .background(Color.Gray), + contentAlignment = Alignment.Center + ) { + Icon(Icons.Default.SmartToy, null, tint = Color.White, modifier = Modifier.size(24.dp)) + } + Spacer(modifier = Modifier.width(12.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + botNames.getOrElse(botIdx) { "机器人${botIdx + 1}" }, + color = Color.White, fontWeight = FontWeight.Bold, fontSize = 16.sp + ) + Text("AI机器人", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp) + } + Box { + IconButton(onClick = { botDropdownExpanded = true }) { + Icon(Icons.Default.ArrowDropDown, "改名", tint = GoldAccent) + } + DropdownMenu( + expanded = botDropdownExpanded, + onDismissRequest = { botDropdownExpanded = false } + ) { + BOT_NAMES.forEach { preset -> + val alreadyUsed = (0 until botIdx).any { botNames.getOrNull(it) == preset } || + (botIdx + 1 until botNames.size).any { botNames.getOrNull(it) == preset } + DropdownMenuItem( + text = { Text(preset, color = if (alreadyUsed) Color.Gray else Color.White) }, + onClick = { + if (!alreadyUsed) { + val newList = botNames.toMutableList() + newList[botIdx] = preset + botNames = newList + } + botDropdownExpanded = false + }, + enabled = !alreadyUsed + ) + } + } + } + } + } } Spacer(modifier = Modifier.height(24.dp)) @@ -255,7 +306,7 @@ fun LocalSetupScreen( onClick = { com.unogame.game.AIDifficulty.save(context, difficulty) com.unogame.game.GameRules.saveMaxHandSize(context, mode, maxHandSize) - onStartGame(totalPlayers, name.ifBlank { "玩家" }) + onStartGame(totalPlayers, name.ifBlank { "玩家" }, botNames.take(totalPlayers - 1)) }, modifier = Modifier.fillMaxWidth().height(56.dp), shape = RoundedCornerShape(16.dp),