332 lines
13 KiB
Kotlin
332 lines
13 KiB
Kotlin
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
|
||
)
|
||
}
|
||
}
|
||
}
|
||
}
|