fix: 出牌记录改为米白背景+黑色文字+彩色底色、万能牌→颜色整体着色、自动换行

This commit is contained in:
flykhan 2026-04-26 20:41:42 +08:00
parent 347e01b5ee
commit 13a1e9432a
2 changed files with 116 additions and 22 deletions

View File

@ -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

View File

@ -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<GameState?>(null) }
var pendingDrawCardIdxForWild by remember { mutableIntStateOf(-1) }
var gameLog by remember { mutableStateOf(listOf<String>()) }
// 记录每条日志对应的玩家名,用于行着色
var gameLogPlayers by remember { mutableStateOf(listOf<String>()) }
var showLogDialog by remember { mutableStateOf(false) }
var showSwapTargetPicker by remember { mutableStateOf(false) }
var pendingSwapState by remember { mutableStateOf<GameState?>(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<String, Color>? {
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
)
}