UnoGame/app/src/main/java/com/unogame/ui/screens/LocalSetupScreen.kt

332 lines
13 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.unogame.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.unogame.ui.theme.*
fun pickRandomHumanName(): String = HUMAN_NAME_PRESETS.random()
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocalSetupScreen(
playerName: String,
modeDisplayName: String,
mode: com.unogame.game.GameMode,
onStartGame: (Int, String) -> Unit,
onBack: () -> Unit
) {
var totalPlayers by remember { mutableIntStateOf(2) }
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 = remember { pickUniqueBotNames(3) }
Box(
modifier = Modifier
.fillMaxSize()
.background(LocalTableBg.current.color)
) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(24.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, "返回", tint = Color.White)
}
Text(
"本地模式 - $modeDisplayName",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
Spacer(modifier = Modifier.height(32.dp))
// Player name
Text("你的名字", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = name,
onValueChange = { name = it.take(10) },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedBorderColor = GoldAccent,
unfocusedBorderColor = Color.Gray,
cursorColor = GoldAccent
)
)
Spacer(modifier = Modifier.height(8.dp))
var nameDropdownExpanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = nameDropdownExpanded,
onExpandedChange = { nameDropdownExpanded = it }
) {
OutlinedTextField(
value = "",
onValueChange = {},
readOnly = true,
placeholder = { Text("或选择预设名字", color = Color.White.copy(alpha = 0.4f)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = nameDropdownExpanded) },
modifier = Modifier.fillMaxWidth().menuAnchor(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedBorderColor = GoldAccent,
unfocusedBorderColor = Color.Gray,
cursorColor = GoldAccent
)
)
ExposedDropdownMenu(
expanded = nameDropdownExpanded,
onDismissRequest = { nameDropdownExpanded = false }
) {
HUMAN_NAME_PRESETS.forEach { preset ->
DropdownMenuItem(
text = { Text(preset) },
onClick = {
name = preset
nameDropdownExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(28.dp))
// Player count selector
Text("总玩家数", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
listOf(2, 3, 4).forEach { count ->
val isSelected = totalPlayers == count
Card(
modifier = Modifier
.size(90.dp)
.clickable { totalPlayers = count },
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = if (isSelected) GoldAccent else DarkSurface
)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "$count",
fontSize = 32.sp,
fontWeight = FontWeight.Black,
color = if (isSelected) Color.Black else Color.White
)
Text(
text = "",
fontSize = 12.sp,
color = if (isSelected) Color.Black.copy(alpha = 0.7f) else Color.White.copy(alpha = 0.5f)
)
}
}
}
}
}
Spacer(modifier = Modifier.height(28.dp))
// AI difficulty
Text("AI难度", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
com.unogame.game.AIDifficulty.values().forEach { d ->
val selected = difficulty == d
FilterChip(
selected = selected,
onClick = { difficulty = d },
label = { Text(d.displayName, fontSize = 13.sp) },
colors = FilterChipDefaults.filterChipColors(
selectedContainerColor = when(d) {
com.unogame.game.AIDifficulty.EASY -> UnoGreen
com.unogame.game.AIDifficulty.NORMAL -> GoldAccent
com.unogame.game.AIDifficulty.HARD -> UnoRed
},
selectedLabelColor = Color.Black
)
)
}
}
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))
// You
PlayerSlot(
name = name.ifBlank { "玩家" },
isHuman = true,
isYou = true
)
// Bots
for (i in 1 until totalPlayers) {
Spacer(modifier = Modifier.height(8.dp))
PlayerSlot(
name = botNames[i - 1],
isHuman = false,
isYou = false
)
}
Spacer(modifier = Modifier.height(24.dp))
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),
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = GoldAccent,
contentColor = Color.Black
)
) {
Icon(Icons.Default.PlayArrow, null)
Spacer(modifier = Modifier.width(8.dp))
Text("开始游戏", fontSize = 18.sp, fontWeight = FontWeight.Bold)
}
Spacer(modifier = Modifier.height(16.dp))
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = DarkSurface.copy(alpha = 0.5f))
) {
Text(
"• 1位真人 + ${totalPlayers - 1}个机器人\n• 支持2-4人局\n• 机器人会自动出牌,无需等待",
color = Color.White.copy(alpha = 0.5f),
fontSize = 12.sp,
lineHeight = 20.sp,
modifier = Modifier.padding(16.dp)
)
}
}
}
}
@Composable
private fun PlayerSlot(
name: String,
isHuman: Boolean,
isYou: Boolean
) {
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(if (isHuman) GoldAccent else Color.Gray),
contentAlignment = Alignment.Center
) {
Icon(
if (isHuman) Icons.Default.Person else Icons.Default.SmartToy,
contentDescription = null,
tint = if (isHuman) Color.Black else Color.White,
modifier = Modifier.size(24.dp)
)
}
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(name, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 16.sp)
Text(
if (isHuman) "真人玩家${if (isYou) " (你)" else ""}" else "AI机器人",
color = Color.White.copy(alpha = 0.5f),
fontSize = 12.sp
)
}
}
}
}