fix: 修复7-0交换手牌bug、Flip卡面匹配bug,新增加摸牌后可出牌弹窗、出牌记录查看、手牌上限设置、昵称实时保存
This commit is contained in:
parent
d201ae48eb
commit
8ac938cb2b
@ -377,6 +377,7 @@ fun UnoApp() {
|
||||
LocalSetupScreen(
|
||||
playerName = savedName,
|
||||
modeDisplayName = mode.displayName,
|
||||
mode = mode,
|
||||
onStartGame = { totalPlayers, name ->
|
||||
if (name.isNotEmpty()) {
|
||||
savedName = name
|
||||
|
||||
@ -122,7 +122,10 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
}
|
||||
|
||||
sealed class PlayResult {
|
||||
data class Success(val state: GameState) : PlayResult()
|
||||
data class Success(
|
||||
val state: GameState,
|
||||
val drawnCardPlayableIndex: Int = -1
|
||||
) : PlayResult()
|
||||
data class Error(val message: String) : PlayResult()
|
||||
}
|
||||
|
||||
@ -301,8 +304,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
sevenZeroDone = true
|
||||
} else if (pendingDraw == -2) {
|
||||
// 7: swap hand with the next player
|
||||
val nextIdx = advanceIndex(state, state.nextPlayerIndex())
|
||||
if (nextIdx != playerIndex) {
|
||||
val nextIdx = state.nextPlayerIndex()
|
||||
val nextCards = state.players[nextIdx].cards
|
||||
modifiedPlayers = state.players.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
@ -311,7 +313,6 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
else -> p
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingDraw = 0
|
||||
sevenZeroDone = true
|
||||
}
|
||||
@ -355,7 +356,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
}
|
||||
|
||||
val calledUno = newCards.size == 1
|
||||
val basePlayers = if (activeCard.type == CardType.SKIP_ALL) modifiedPlayers else state.players
|
||||
val basePlayers = if (activeCard.type == CardType.SKIP_ALL || sevenZeroDone) modifiedPlayers else state.players
|
||||
val updatedPlayers = basePlayers.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
playerIndex -> p.copy(cards = if (sevenZeroDone) p.cards else newCards, isCurrentTurn = false, calledUno = calledUno, cardCount = if (sevenZeroDone) p.cards.size else newCards.size)
|
||||
@ -387,11 +388,12 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
val player = state.players[playerIndex]
|
||||
var drawAmount = if (state.pendingDrawCount > 0) state.pendingDrawCount else 1
|
||||
|
||||
// No Mercy 10-card limit: cap draw to not exceed 10
|
||||
if (rules.mode == GameMode.NO_MERCY) {
|
||||
val remaining = (10 - player.cards.size).coerceAtLeast(0)
|
||||
// Hand size limit: cap draw to not exceed maxHandSize
|
||||
val handLimit = rules.maxHandSize
|
||||
if (handLimit > 0) {
|
||||
val remaining = (handLimit - player.cards.size).coerceAtLeast(0)
|
||||
if (remaining == 0) {
|
||||
val message = "${player.name} 手牌已满10张,跳过摸牌"
|
||||
val message = "${player.name} 手牌已满${handLimit}张,跳过摸牌"
|
||||
val nextIndex = state.nextPlayerIndex()
|
||||
val updatedPlayers = state.players.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
@ -432,7 +434,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
|
||||
val message = if (state.pendingDrawCount > 0) {
|
||||
if (actualDraw < drawAmount && actualDraw < state.pendingDrawCount)
|
||||
"${player.name} 摸了 ${drawnCards.size} 张惩罚牌(已达10张上限)"
|
||||
"${player.name} 摸了 ${drawnCards.size} 张惩罚牌(已达${handLimit}张上限)"
|
||||
else if (actualDraw < drawAmount)
|
||||
"${player.name} 摸了 ${drawnCards.size} 张惩罚牌(牌不够,需${drawAmount}张)"
|
||||
else
|
||||
@ -441,13 +443,60 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
"${player.name} 摸了一张牌"
|
||||
}
|
||||
|
||||
val updatedPlayers = state.players.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
playerIndex -> p.copy(cards = newCards, isCurrentTurn = false, cardCount = newCards.size)
|
||||
else -> if (i == nextIndex) p.copy(isCurrentTurn = true) else p.copy(isCurrentTurn = false)
|
||||
// For voluntary draws, check if the drawn card can be played
|
||||
var drawnCardPlayableIndex = -1
|
||||
val wasVoluntary = state.pendingDrawCount == 0
|
||||
val topCard = discardPile.lastOrNull()
|
||||
if (wasVoluntary && drawnCards.isNotEmpty() && topCard != null) {
|
||||
val wildColor = state.currentWildColor
|
||||
val flipped = state.flipped
|
||||
val lastDrawn = drawnCards.last()
|
||||
if (lastDrawn.matches(topCard, wildColor, flipped)) {
|
||||
drawnCardPlayableIndex = newCards.size - 1
|
||||
}
|
||||
}
|
||||
|
||||
val updatedPlayers = state.players.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
playerIndex -> p.copy(
|
||||
cards = newCards,
|
||||
isCurrentTurn = drawnCardPlayableIndex >= 0,
|
||||
cardCount = newCards.size
|
||||
)
|
||||
else -> if (!wasVoluntary || drawnCardPlayableIndex < 0) {
|
||||
if (i == nextIndex) p.copy(isCurrentTurn = true) else p.copy(isCurrentTurn = false)
|
||||
} else {
|
||||
p.copy(isCurrentTurn = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val finalNextIndex = if (drawnCardPlayableIndex >= 0) playerIndex else nextIndex
|
||||
|
||||
return PlayResult.Success(
|
||||
state = state.copy(
|
||||
players = updatedPlayers,
|
||||
currentPlayerIndex = finalNextIndex,
|
||||
discardPile = discardPile.toList(),
|
||||
drawPileCount = drawPile.size,
|
||||
currentWildColor = state.currentWildColor,
|
||||
pendingDrawCount = if (drawnCardPlayableIndex >= 0) state.pendingDrawCount else 0,
|
||||
flipped = state.flipped,
|
||||
turnNumber = state.turnNumber + 1,
|
||||
message = message
|
||||
),
|
||||
drawnCardPlayableIndex = drawnCardPlayableIndex
|
||||
)
|
||||
}
|
||||
|
||||
fun skipAfterDraw(state: GameState): PlayResult {
|
||||
val nextIndex = state.nextPlayerIndex()
|
||||
val updatedPlayers = state.players.mapIndexed { i, p ->
|
||||
when (i) {
|
||||
state.currentPlayerIndex -> p.copy(isCurrentTurn = false)
|
||||
else -> if (i == nextIndex) p.copy(isCurrentTurn = true) else p.copy(isCurrentTurn = false)
|
||||
}
|
||||
}
|
||||
return PlayResult.Success(
|
||||
state.copy(
|
||||
players = updatedPlayers,
|
||||
@ -458,7 +507,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
pendingDrawCount = 0,
|
||||
flipped = state.flipped,
|
||||
turnNumber = state.turnNumber + 1,
|
||||
message = message
|
||||
message = "${state.currentPlayer.name} 选择不出牌"
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -491,9 +540,9 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
if (target.cardCount != 1) return PlayResult.Error("该玩家不止1张牌")
|
||||
if (target.calledUno) return PlayResult.Error("该玩家已经喊过UNO了")
|
||||
var drawPenalty = 2
|
||||
// No Mercy 10-card limit: skip penalty if at limit, cap if near limit
|
||||
if (rules.mode == GameMode.NO_MERCY) {
|
||||
val remaining = (10 - target.cards.size).coerceAtLeast(0)
|
||||
val handLimit = rules.maxHandSize
|
||||
if (handLimit > 0) {
|
||||
val remaining = (handLimit - target.cards.size).coerceAtLeast(0)
|
||||
if (remaining == 0) {
|
||||
val updatedPlayers = state.players.mapIndexed { i, p ->
|
||||
if (i == targetIdx) p.copy(calledUno = true) else p
|
||||
@ -502,7 +551,7 @@ class GameEngine(private val rules: GameRules = GameRules.forMode(GameMode.NORMA
|
||||
players = updatedPlayers,
|
||||
discardPile = discardPile.toList(),
|
||||
drawPileCount = drawPile.size,
|
||||
message = "${state.players[challengerIdx].name} 抓住了 ${target.name} 没喊UNO!(手牌已满10张,免罚)",
|
||||
message = "${state.players[challengerIdx].name} 抓住了 ${target.name} 没喊UNO!(手牌已满${handLimit}张,免罚)",
|
||||
turnNumber = state.turnNumber + 1
|
||||
))
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.unogame.game
|
||||
|
||||
import android.content.Context
|
||||
import com.unogame.model.*
|
||||
|
||||
enum class GameMode(val displayName: String) {
|
||||
@ -15,9 +16,12 @@ data class GameRules(
|
||||
val allowStacking: Boolean,
|
||||
val colors: List<CardColor>,
|
||||
val usesFlip: Boolean,
|
||||
val usesNoMercyCards: Boolean
|
||||
val usesNoMercyCards: Boolean,
|
||||
val maxHandSize: Int = 0 // 0 = no limit
|
||||
) {
|
||||
companion object {
|
||||
private const val PREF_KEY_PREFIX = "handsize_"
|
||||
|
||||
fun forMode(mode: GameMode): GameRules = when (mode) {
|
||||
GameMode.NORMAL -> GameRules(
|
||||
mode = mode,
|
||||
@ -52,5 +56,24 @@ data class GameRules(
|
||||
usesNoMercyCards = false
|
||||
)
|
||||
}
|
||||
|
||||
fun forMode(mode: GameMode, maxHandSize: Int): GameRules {
|
||||
return forMode(mode).copy(maxHandSize = maxHandSize)
|
||||
}
|
||||
|
||||
fun loadMaxHandSize(context: Context, mode: GameMode): Int {
|
||||
return context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||
.getInt(PREF_KEY_PREFIX + mode.name, defaultMaxHandSize(mode))
|
||||
}
|
||||
|
||||
fun saveMaxHandSize(context: Context, mode: GameMode, size: Int) {
|
||||
context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||
.edit().putInt(PREF_KEY_PREFIX + mode.name, size).apply()
|
||||
}
|
||||
|
||||
fun defaultMaxHandSize(mode: GameMode): Int = when (mode) {
|
||||
GameMode.NO_MERCY -> 10
|
||||
else -> 0 // no limit for other modes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ data class Card(
|
||||
if (c.color == CardColor.WILD) return true
|
||||
if (o.color == CardColor.WILD)
|
||||
return currentWildColor == null || c.color == currentWildColor
|
||||
if (c.type == CardType.FLIP || o.type == CardType.FLIP) return true
|
||||
if (c.color == o.color) return true
|
||||
if (c.type == CardType.NUMBER && o.type == CardType.NUMBER && c.number == o.number)
|
||||
return true
|
||||
|
||||
@ -33,6 +33,7 @@ fun GameScreen(
|
||||
onChooseColor: (CardColor) -> Unit,
|
||||
onCallUno: () -> Unit = {},
|
||||
onChallengeUno: (String) -> Unit = {},
|
||||
onShowLog: () -> Unit = {},
|
||||
errorMessage: String
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
@ -62,17 +63,29 @@ fun GameScreen(
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.padding(horizontal = 16.dp)
|
||||
.clickable { onShowLog() },
|
||||
colors = CardDefaults.cardColors(containerColor = DarkSurface),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = gameState.message,
|
||||
color = GoldAccent,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Icon(
|
||||
Icons.Default.List,
|
||||
null,
|
||||
tint = Color.White.copy(alpha = 0.4f),
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,22 @@
|
||||
package com.unogame.ui.screens
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
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.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.unogame.game.AIDifficulty
|
||||
@ -11,6 +25,7 @@ import com.unogame.game.GameMode
|
||||
import com.unogame.game.GameRules
|
||||
import com.unogame.game.SimpleAI
|
||||
import com.unogame.model.*
|
||||
import com.unogame.ui.theme.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -78,7 +93,7 @@ fun LocalGameScreen(
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val rules = remember { GameRules.forMode(mode) }
|
||||
val rules = remember { GameRules.forMode(mode, GameRules.loadMaxHandSize(context, mode)) }
|
||||
val engine = remember { GameEngine(rules) }
|
||||
val aiDiff = remember { AIDifficulty.load(context) }
|
||||
val gameStartTime = remember { System.currentTimeMillis() }
|
||||
@ -104,6 +119,11 @@ fun LocalGameScreen(
|
||||
var gameDuration by remember { mutableIntStateOf(0) }
|
||||
var gameDifficulty by remember { mutableStateOf("") }
|
||||
var gameTurnNumber by remember { mutableIntStateOf(0) }
|
||||
var showDrawPlayPopup by remember { mutableStateOf(false) }
|
||||
var pendingDrawState by remember { mutableStateOf<GameState?>(null) }
|
||||
var pendingDrawnCardIndex by remember { mutableIntStateOf(-1) }
|
||||
var gameLog by remember { mutableStateOf(listOf<String>()) }
|
||||
var showLogDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val myPlayerId = "human"
|
||||
val currentPlayer = gameState.currentPlayer
|
||||
@ -113,6 +133,9 @@ fun LocalGameScreen(
|
||||
gameState = state
|
||||
myCards = state.players.find { it.id == myPlayerId }?.cards ?: myCards
|
||||
errorMessage = ""
|
||||
if (state.message.isNotEmpty()) {
|
||||
gameLog = gameLog + state.message
|
||||
}
|
||||
if (state.isGameOver) {
|
||||
isGameOver = true
|
||||
winnerName = state.winner?.name ?: ""
|
||||
@ -153,7 +176,15 @@ fun LocalGameScreen(
|
||||
fun executeDraw() {
|
||||
val result = engine.drawCard(gameState, myPlayerId)
|
||||
when (result) {
|
||||
is GameEngine.PlayResult.Success -> updateState(result.state)
|
||||
is GameEngine.PlayResult.Success -> {
|
||||
if (result.drawnCardPlayableIndex >= 0) {
|
||||
pendingDrawState = result.state
|
||||
pendingDrawnCardIndex = result.drawnCardPlayableIndex
|
||||
showDrawPlayPopup = true
|
||||
} else {
|
||||
updateState(result.state)
|
||||
}
|
||||
}
|
||||
is GameEngine.PlayResult.Error -> errorMessage = result.message
|
||||
}
|
||||
}
|
||||
@ -177,7 +208,19 @@ fun LocalGameScreen(
|
||||
}
|
||||
SimpleAI.MoveType.DRAW -> {
|
||||
val drawResult = engine.drawCard(gameState, current.id)
|
||||
if (drawResult is GameEngine.PlayResult.Success) updateState(drawResult.state)
|
||||
if (drawResult is GameEngine.PlayResult.Success) {
|
||||
if (drawResult.drawnCardPlayableIndex >= 0) {
|
||||
val playResult = engine.playCard(drawResult.state, current.id, drawResult.drawnCardPlayableIndex)
|
||||
if (playResult is GameEngine.PlayResult.Success) {
|
||||
updateState(playResult.state)
|
||||
} else {
|
||||
val skipResult = engine.skipAfterDraw(drawResult.state)
|
||||
if (skipResult is GameEngine.PlayResult.Success) updateState(skipResult.state)
|
||||
}
|
||||
} else {
|
||||
updateState(drawResult.state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,6 +231,83 @@ fun LocalGameScreen(
|
||||
myCards.map { it.activeCard(gameState.flipped) }
|
||||
}
|
||||
|
||||
// Draw-then-play popup
|
||||
if (showDrawPlayPopup && pendingDrawState != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
val state = pendingDrawState!!
|
||||
val result = engine.skipAfterDraw(state)
|
||||
if (result is GameEngine.PlayResult.Success) updateState(result.state)
|
||||
showDrawPlayPopup = false
|
||||
},
|
||||
title = { Text("摸到了可出的牌", color = GoldAccent) },
|
||||
text = { Text("刚摸到的牌可以打出去,要出这张牌吗?", color = Color.White.copy(alpha = 0.8f)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
val state = pendingDrawState!!
|
||||
val cardIdx = pendingDrawnCardIndex
|
||||
showDrawPlayPopup = false
|
||||
pendingDrawState = null
|
||||
if (cardIdx >= 0) {
|
||||
val result = engine.playCard(state, myPlayerId, cardIdx)
|
||||
when (result) {
|
||||
is GameEngine.PlayResult.Success -> updateState(result.state)
|
||||
is GameEngine.PlayResult.Error -> errorMessage = result.message
|
||||
}
|
||||
}
|
||||
}) { Text("出牌", color = GoldAccent) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = {
|
||||
val state = pendingDrawState!!
|
||||
val result = engine.skipAfterDraw(state)
|
||||
if (result is GameEngine.PlayResult.Success) updateState(result.state)
|
||||
showDrawPlayPopup = false
|
||||
}) { Text("跳过", color = Color.White.copy(alpha = 0.5f)) }
|
||||
},
|
||||
containerColor = DarkSurface
|
||||
)
|
||||
}
|
||||
|
||||
// Game log dialog
|
||||
if (showLogDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showLogDialog = false },
|
||||
title = { Text("出牌记录", color = GoldAccent, fontWeight = FontWeight.Bold) },
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.heightIn(max = 400.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
if (gameLog.isEmpty()) {
|
||||
Text("暂无记录", color = Color.White.copy(alpha = 0.5f))
|
||||
}
|
||||
gameLog.forEachIndexed { index, msg ->
|
||||
Row(modifier = Modifier.padding(vertical = 2.dp)) {
|
||||
Text(
|
||||
"${index + 1}. ",
|
||||
color = Color.White.copy(alpha = 0.3f),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
Text(
|
||||
msg,
|
||||
color = Color.White.copy(alpha = 0.8f),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showLogDialog = false }) {
|
||||
Text("关闭", color = GoldAccent)
|
||||
}
|
||||
},
|
||||
containerColor = DarkSurface
|
||||
)
|
||||
}
|
||||
|
||||
if (isGameOver) {
|
||||
GameOverScreen(
|
||||
winnerName = winnerName,
|
||||
@ -209,6 +329,7 @@ fun LocalGameScreen(
|
||||
onPlayCard = { index -> executePlay(index, selectedWildColor) },
|
||||
onDrawCard = { executeDraw() },
|
||||
onChooseColor = { selectedWildColor = it },
|
||||
onShowLog = { showLogDialog = true },
|
||||
onCallUno = {
|
||||
// Mark player as having called UNO
|
||||
val updated = gameState.players.map {
|
||||
|
||||
@ -26,6 +26,7 @@ import com.unogame.ui.theme.*
|
||||
fun LocalSetupScreen(
|
||||
playerName: String,
|
||||
modeDisplayName: String,
|
||||
mode: com.unogame.game.GameMode,
|
||||
onStartGame: (Int, String) -> Unit,
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
@ -33,6 +34,8 @@ fun LocalSetupScreen(
|
||||
var name by remember { mutableStateOf(playerName) }
|
||||
var difficulty by remember { mutableStateOf(com.unogame.game.AIDifficulty.NORMAL) }
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
val initMaxHandSize = remember { com.unogame.game.GameRules.loadMaxHandSize(context, mode) }
|
||||
var maxHandSize by remember { mutableIntStateOf(initMaxHandSize) }
|
||||
|
||||
val botNames = listOf("🤖 机器人1", "🤖 机器人2", "🤖 机器人3")
|
||||
|
||||
@ -152,6 +155,41 @@ fun LocalSetupScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
|
||||
// Hand size limit
|
||||
Text("手牌上限", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Slider(
|
||||
value = maxHandSize.toFloat(),
|
||||
onValueChange = { maxHandSize = it.toInt() },
|
||||
valueRange = 0f..20f,
|
||||
steps = 19,
|
||||
modifier = Modifier.weight(1f),
|
||||
colors = SliderDefaults.colors(
|
||||
thumbColor = GoldAccent,
|
||||
activeTrackColor = GoldAccent
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = if (maxHandSize == 0) "无限制" else "${maxHandSize}张",
|
||||
color = GoldAccent,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.width(56.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"0 = 无限制,默认各模式不限制,无情模式默认10张上限",
|
||||
color = Color.White.copy(alpha = 0.35f),
|
||||
fontSize = 11.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
|
||||
// Player list preview
|
||||
Text("参与者预览", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@ -178,6 +216,7 @@ fun LocalSetupScreen(
|
||||
Button(
|
||||
onClick = {
|
||||
com.unogame.game.AIDifficulty.save(context, difficulty)
|
||||
com.unogame.game.GameRules.saveMaxHandSize(context, mode, maxHandSize)
|
||||
onStartGame(totalPlayers, name.ifBlank { "玩家" })
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().height(56.dp),
|
||||
|
||||
@ -74,7 +74,10 @@ fun MainMenuScreen(
|
||||
// Name input
|
||||
OutlinedTextField(
|
||||
value = playerName,
|
||||
onValueChange = { playerName = it.take(10) },
|
||||
onValueChange = {
|
||||
playerName = it.take(10)
|
||||
onNameChanged(playerName.ifBlank { "玩家" })
|
||||
},
|
||||
label = { Text("你的昵称", color = Color.White.copy(alpha = 0.6f)) },
|
||||
singleLine = true,
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
|
||||
@ -147,7 +147,11 @@ fun RulesHelpScreen(onBack: () -> Unit) {
|
||||
Label("UNO叫牌与抓人")
|
||||
Bullet("出牌后手牌剩1张时,红色\"喊UNO!\"按钮出现,必须点击")
|
||||
Bullet("未喊且被其他玩家\"抓XX!\",罚抽2张")
|
||||
Bullet("若已被抓过或手牌已满10张(无情模式),免罚")
|
||||
Bullet("若已被抓过或手牌已满上限(无情模式),免罚")
|
||||
SpacerH(8)
|
||||
Label("操作提示")
|
||||
Bullet("长按手牌可查看牌面说明与分数")
|
||||
Bullet("点击出牌记录可以查看完整出牌历史")
|
||||
SpacerH(8)
|
||||
Label("游戏结束与计分")
|
||||
Bullet("先出完手牌者获胜,单局结束")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user