From d201ae48eb6673e81156a3b5f4c6982e6c6cfdeb Mon Sep 17 00:00:00 2001 From: flykhan Date: Sun, 26 Apr 2026 11:01:16 +0800 Subject: [PATCH] =?UTF-8?q?fix=20:=20=E4=BF=AE=E5=A4=8D=E7=A7=AF=E5=88=86?= =?UTF-8?q?=E6=A6=9C=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/unogame/ui/screens/GameOverScreen.kt | 41 +++++++- .../com/unogame/ui/screens/LocalGameScreen.kt | 76 +++++++++++--- .../unogame/ui/screens/ScoreboardScreen.kt | 98 +++++++++++++------ gradlew | 2 + 4 files changed, 175 insertions(+), 42 deletions(-) create mode 100755 gradlew diff --git a/app/src/main/java/com/unogame/ui/screens/GameOverScreen.kt b/app/src/main/java/com/unogame/ui/screens/GameOverScreen.kt index cb35586..d6c90f0 100644 --- a/app/src/main/java/com/unogame/ui/screens/GameOverScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/GameOverScreen.kt @@ -21,8 +21,15 @@ import com.unogame.ui.theme.* fun GameOverScreen( winnerName: String, isYouWinner: Boolean, - onBackToMenu: () -> Unit + onBackToMenu: () -> Unit, + points: Int = 0, + difficulty: String = "", + duration: Int = 0, + turnNumber: Int = 0, + playerCount: Int = 0 ) { + val durationText = if (duration >= 60) "${duration / 60}分${duration % 60}秒" else "${duration}秒" + Box( modifier = Modifier .fillMaxSize() @@ -61,6 +68,38 @@ fun GameOverScreen( textAlign = TextAlign.Center ) + if (isYouWinner && points > 0) { + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = "+$points 分", + fontSize = 32.sp, + fontWeight = FontWeight.Black, + color = GoldAccent + ) + Spacer(modifier = Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("难度", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp) + Text(difficulty, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("时长", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp) + Text(durationText, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("人数", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp) + Text("${playerCount}人", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("轮次", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp) + Text("$turnNumber 轮", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold) + } + } + } + Spacer(modifier = Modifier.height(48.dp)) Button( 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 fe6c331..eba9b27 100644 --- a/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/LocalGameScreen.kt @@ -14,10 +14,19 @@ import com.unogame.model.* import kotlinx.coroutines.delay import kotlinx.coroutines.launch -data class ScoreEntry(val name: String, val wins: Int, val mode: String) +data class ScoreEntry( + val name: String, + val mode: String, + val points: Int, + val difficulty: String, + val duration: Int, + val playerCount: Int, + val turnNumber: Int, + val date: Long +) object Scoreboard { - private const val PREFS_KEY = "uno_scores" + private const val PREFS_KEY = "uno_scoreboard" private val gson = Gson() fun loadScores(context: Context): List { @@ -33,15 +42,30 @@ object Scoreboard { .edit().putString(PREFS_KEY, gson.toJson(scores)).apply() } - fun addWin(context: Context, name: String, mode: GameMode) { + fun addEntry( + context: Context, + name: String, + mode: String, + points: Int, + difficulty: String, + duration: Int, + playerCount: Int, + turnNumber: Int + ) { val scores = loadScores(context).toMutableList() - val existing = scores.indexOfFirst { it.name == name && it.mode == mode.displayName } - if (existing >= 0) { - scores[existing] = scores[existing].copy(wins = scores[existing].wins + 1) - } else { - scores.add(ScoreEntry(name, 1, mode.displayName)) - } - saveScores(context, scores.sortedByDescending { it.wins }.take(20)) + scores.add( + ScoreEntry( + name = name, + mode = mode, + points = points, + difficulty = difficulty, + duration = duration, + playerCount = playerCount, + turnNumber = turnNumber, + date = System.currentTimeMillis() + ) + ) + saveScores(context, scores.sortedByDescending { it.points }.take(50)) } } @@ -57,6 +81,7 @@ fun LocalGameScreen( val rules = remember { GameRules.forMode(mode) } val engine = remember { GameEngine(rules) } val aiDiff = remember { AIDifficulty.load(context) } + val gameStartTime = remember { System.currentTimeMillis() } val players = remember { val list = mutableListOf() @@ -75,6 +100,10 @@ fun LocalGameScreen( var winnerName by remember { mutableStateOf("") } var isYouWinner by remember { mutableStateOf(false) } var scoreSaved by remember { mutableStateOf(false) } + var gamePoints by remember { mutableIntStateOf(0) } + var gameDuration by remember { mutableIntStateOf(0) } + var gameDifficulty by remember { mutableStateOf("") } + var gameTurnNumber by remember { mutableIntStateOf(0) } val myPlayerId = "human" val currentPlayer = gameState.currentPlayer @@ -90,7 +119,25 @@ fun LocalGameScreen( isYouWinner = state.winner?.id == myPlayerId if (isYouWinner && !scoreSaved) { scoreSaved = true - Scoreboard.addWin(context, humanPlayerName, mode) + gameTurnNumber = state.turnNumber + gameDuration = ((System.currentTimeMillis() - gameStartTime) / 1000).toInt() + gameDifficulty = aiDiff.displayName + gamePoints = state.players.filter { it.id != myPlayerId }.sumOf { player -> + player.cards.sumOf { card -> + val active = if (state.flipped && card.flipSide != null) card.flipSide else card + active.score + } + } + Scoreboard.addEntry( + context = context, + name = humanPlayerName, + mode = mode.displayName, + points = gamePoints, + difficulty = gameDifficulty, + duration = gameDuration, + playerCount = totalPlayers, + turnNumber = gameTurnNumber + ) } } } @@ -145,7 +192,12 @@ fun LocalGameScreen( GameOverScreen( winnerName = winnerName, isYouWinner = isYouWinner, - onBackToMenu = onBackToMenu + onBackToMenu = onBackToMenu, + points = gamePoints, + difficulty = gameDifficulty, + duration = gameDuration, + turnNumber = gameTurnNumber, + playerCount = totalPlayers ) } else { GameScreen( diff --git a/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt b/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt index 80423e0..856382d 100644 --- a/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt @@ -1,7 +1,9 @@ package com.unogame.ui.screens import androidx.compose.foundation.background +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape @@ -19,6 +21,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.unogame.game.GameMode import com.unogame.ui.theme.* +import java.text.SimpleDateFormat +import java.util.* @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,7 +52,9 @@ fun ScoreboardScreen(onBack: () -> Unit) { // Mode filter chips if (scores.isNotEmpty()) { Row( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { modes.forEach { mode -> @@ -95,41 +101,75 @@ fun ScoreboardScreen(onBack: () -> Unit) { 2 -> Color(0xFFCD7F32) else -> Color.White.copy(alpha = 0.5f) } + val dateFormat = remember { SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()) } + val durationText = if (entry.duration >= 60) "${entry.duration / 60}分${entry.duration % 60}秒" + else "${entry.duration}秒" + Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp), colors = CardDefaults.cardColors(containerColor = if (index < 3) DarkSurface else DarkCard) ) { - Row( - modifier = Modifier.padding(12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // Rank - Text( - text = "#${index + 1}", - color = rankColor, - fontSize = 18.sp, - fontWeight = FontWeight.Black, - modifier = Modifier.width(44.dp) - ) - // Name - Column(modifier = Modifier.weight(1f)) { - Text(entry.name, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 15.sp) - Text( - entry.mode, - color = Color.White.copy(alpha = 0.5f), - fontSize = 12.sp - ) - } - // Wins + Column(modifier = Modifier.padding(12.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Default.Star, null, tint = GoldAccent, modifier = Modifier.size(18.dp)) - Spacer(modifier = Modifier.width(4.dp)) Text( - "${entry.wins}胜", - color = GoldAccent, - fontWeight = FontWeight.Bold, - fontSize = 16.sp + text = "#${index + 1}", + color = rankColor, + fontSize = 18.sp, + fontWeight = FontWeight.Black, + modifier = Modifier.width(44.dp) + ) + Column(modifier = Modifier.weight(1f)) { + Text(entry.name, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 15.sp) + Text( + entry.mode, + color = GoldAccent, + fontSize = 12.sp + ) + } + Column(horizontalAlignment = Alignment.End) { + Text( + "${entry.points}分", + color = GoldAccent, + fontWeight = FontWeight.Black, + fontSize = 20.sp + ) + } + } + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.SmartToy, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Spacer(modifier = Modifier.width(2.dp)) + Text(entry.difficulty, color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp) + } + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.Timer, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Spacer(modifier = Modifier.width(2.dp)) + Text(durationText, color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp) + } + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.Group, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Spacer(modifier = Modifier.width(2.dp)) + Text("${entry.playerCount}人", color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp) + } + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.Loop, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Spacer(modifier = Modifier.width(2.dp)) + Text("${entry.turnNumber}轮", color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp) + } + } + Spacer(modifier = Modifier.height(4.dp)) + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.Schedule, null, tint = Color.White.copy(alpha = 0.3f), modifier = Modifier.size(12.dp)) + Spacer(modifier = Modifier.width(2.dp)) + Text( + dateFormat.format(Date(entry.date)), + color = Color.White.copy(alpha = 0.3f), + fontSize = 10.sp ) } } diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..7f37a59 --- /dev/null +++ b/gradlew @@ -0,0 +1,2 @@ +#!/bin/sh +exec java -jar "$(dirname "$0")/gradle/wrapper/gradle-wrapper.jar" "$@"