diff --git a/app/src/main/java/com/unogame/game/GameEngine.kt b/app/src/main/java/com/unogame/game/GameEngine.kt index c2de12a..79ba04e 100644 --- a/app/src/main/java/com/unogame/game/GameEngine.kt +++ b/app/src/main/java/com/unogame/game/GameEngine.kt @@ -218,12 +218,12 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA } CardType.WILD -> { wildColor = chosenColor ?: CardColor.RED - message += ",选择 ${wildColor!!.displayName}" + message += " → ${wildColor!!.displayName}" } CardType.WILD_DRAW_FOUR -> { wildColor = chosenColor ?: CardColor.RED pendingDraw = if (isStacking) pendingDraw + 4 else 4 - message += ",下家摸${pendingDraw}张牌,选${wildColor!!.displayName}" + message += " → ${wildColor!!.displayName},下家摸${pendingDraw}张牌" } // Flip mode dark side effects CardType.SKIP_ALL -> { @@ -238,7 +238,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA CardType.WILD_DRAW_TWO -> { wildColor = chosenColor ?: CardColor.RED pendingDraw = 2 - message += ",下家摸2张,选${wildColor!!.displayName}" + message += " → ${wildColor!!.displayName},下家摸2张" } CardType.FLIP -> { flipped = !flipped @@ -260,7 +260,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA pendingDraw = 4 val tempState = state.copy(direction = direction) nextIndex = tempState.nextPlayerIndex() - message += "+4反转,选${wildColor!!.displayName}" + message += " → ${wildColor!!.displayName},+4反转" } CardType.DISCARD_COLOR -> { val discardColor = card.activeCard(flipped).color @@ -274,7 +274,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA CardType.WILD_DRAW_COLOR -> { wildColor = chosenColor ?: CardColor.RED nextIndex = advanceIndex(state, nextIndex) - message += ",跳过下家并选${wildColor!!.displayName}" + message += " → ${wildColor!!.displayName},跳过下家" } CardType.NUMBER -> { // 7-0 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 7a18a90..77c9aae 100644 --- a/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt @@ -14,7 +14,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.gson.Gson @@ -26,6 +29,7 @@ import com.unogame.game.GameRules import com.unogame.game.SimpleAI import com.unogame.model.* import com.unogame.ui.components.ColorPickerDialog +import com.unogame.ui.components.getBotAvatar import com.unogame.ui.theme.* import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -149,6 +153,8 @@ fun LocalGameScreen( var pendingDrawStateForWild by remember { mutableStateOf(null) } var pendingDrawCardIdxForWild by remember { mutableIntStateOf(-1) } var gameLog by remember { mutableStateOf(listOf()) } + // 记录每条日志对应的玩家名,用于行着色 + var gameLogPlayers by remember { mutableStateOf(listOf()) } var showLogDialog by remember { mutableStateOf(false) } var showSwapTargetPicker by remember { mutableStateOf(false) } var pendingSwapState by remember { mutableStateOf(null) } @@ -162,7 +168,10 @@ fun LocalGameScreen( myCards = state.players.find { it.id == myPlayerId }?.cards ?: myCards errorMessage = "" if (state.message.isNotEmpty()) { + // 倒序插入,最新在上 gameLog = listOf(state.message) + gameLog + // 记录该消息对应的操作玩家(消息格式: "玩家名 出了..." 或 "玩家名 摸了...") + gameLogPlayers = listOf(state.currentPlayer.name) + gameLogPlayers } if (state.isGameOver) { isGameOver = true @@ -329,42 +338,127 @@ fun LocalGameScreen( ) } - // Game log dialog + // 出牌记录弹窗 if (showLogDialog) { + // 卡牌颜色名映射 + val cardColorMap = mapOf( + "红" to UnoRed, "蓝" to UnoBlue, + "黄" to Color(0xFFFDD835), "绿" to UnoGreen, + "粉" to Color(0xFFE91E63), "紫" to Color(0xFF9C27B0), + "青" to Color(0xFF009688), "橙" to Color(0xFFFF9800), + "万能" to Color(0xFF616161), "黑" to Color(0xFF424242) + ) + fun parsePlayerName(msg: String): String = msg.substringBefore(" 出了").substringBefore(" 摸了").substringBefore(" 选择").substringBefore(" 无法") + fun parseColorText(msg: String): String { + val after = msg.substringAfter(" 出了 ", "") + if (after.isEmpty()) return "" + val colorNames = listOf("万能", "红", "蓝", "黄", "绿", "粉", "紫", "青", "橙", "黑") + return colorNames.firstOrNull { after.startsWith(it) } ?: "" + } + fun parseArrowColor(msg: String): Pair? { + val arrowIdx = msg.indexOf(" → ") + if (arrowIdx < 0) return null + val afterArrow = msg.substring(arrowIdx + 3).trimStart() + val cn = listOf("红", "蓝", "黄", "绿", "粉", "紫", "青", "橙", "黑", "万能") + for (name in cn) { + if (afterArrow.startsWith(name)) { + return name to (cardColorMap[name] ?: Color.White) + } + } + return null + } + + val creamBg = Color(0xFFFFF8E1) // 护眼米白 + val dimText = Color(0xFF555555) // 浅色底上的暗灰文字 + + // 关闭 + AlertDialog( onDismissRequest = { showLogDialog = false }, - title = { Text("出牌记录", color = GoldAccent, fontWeight = FontWeight.Bold) }, + title = { Text("出牌记录", color = Color(0xFF333333), fontWeight = FontWeight.Bold) }, text = { Column( modifier = Modifier - .heightIn(max = 400.dp) + .heightIn(min = 300.dp, max = 420.dp) .verticalScroll(rememberScrollState()) ) { if (gameLog.isEmpty()) { - Text("暂无记录", color = Color.White.copy(alpha = 0.5f)) + Text("暂无记录", color = dimText, fontSize = 13.sp) } gameLog.forEachIndexed { index, msg -> - Row(modifier = Modifier.padding(vertical = 2.dp)) { - Text( - "${gameLog.size - index}. ", - color = Color.White.copy(alpha = 0.3f), - fontSize = 12.sp - ) - Text( - msg, - color = Color.White.copy(alpha = 0.8f), - fontSize = 12.sp - ) + val playerName = gameLogPlayers.getOrElse(index) { parsePlayerName(msg) } + val avatar = getBotAvatar(playerName) + val colorText = parseColorText(msg) + val cardColor = cardColorMap[colorText] + val isPlay = " 出了 " in msg + val arrowColor = parseArrowColor(msg) + val afterPlayer = msg.substringAfter(playerName) + + // 用 AnnotatedString 构建整行,支持自动换行 + val annotated = buildAnnotatedString { + // 序号 + withStyle(SpanStyle(color = Color(0xFF999999), fontSize = 13.sp)) { + append("${gameLog.size - index}. ") + } + // 玩家名 + withStyle(SpanStyle(color = avatar.color, fontWeight = FontWeight.Bold, fontSize = 13.sp)) { + append(playerName) + } + if (isPlay && (colorText.isNotEmpty() || arrowColor != null)) { + if (arrowColor != null) { + val prefix = afterPlayer.substringBefore(" 万能") + val cardBlock = afterPlayer.substringAfter(" 出了 ").substringBefore(",") + val remain = afterPlayer.substringAfter(prefix + " 出了 " + cardBlock) + withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) { + append(prefix) + append(" 出了 ") + } + withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black, + fontSize = 13.sp, background = arrowColor.second.copy(alpha = 0.9f))) { + append(cardBlock) + } + if (remain.isNotEmpty()) { + withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) { + append(remain) + } + } + } else { + val prefix = afterPlayer.substringBefore(colorText) + val cardId = colorText + afterPlayer.substringAfter(colorText).substringBefore(",") + val remain = afterPlayer.substringAfter(prefix + cardId) + withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) { + append(prefix) + } + withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black, + fontSize = 13.sp, background = (cardColor ?: Color.Gray).copy(alpha = 0.9f))) { + append(cardId) + } + if (remain.isNotEmpty()) { + withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) { + append(remain) + } + } + } + } else { + withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) { + append(afterPlayer) + } + } + } + Text(annotated, modifier = Modifier.padding(vertical = 5.dp)) + // 条目间加细分割线 + if (index < gameLog.size - 1) { + Spacer(modifier = Modifier.height(1.dp)) } } } }, confirmButton = { TextButton(onClick = { showLogDialog = false }) { - Text("关闭", color = GoldAccent) + Text("关闭", color = Color(0xFF333333)) } }, - containerColor = DarkSurface + containerColor = creamBg ) }