Show live table action status
This commit is contained in:
@@ -34,6 +34,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -50,6 +51,7 @@ fun TableScreen(vm: PokerViewModel) {
|
||||
// Only show an action bar that belongs to the table currently on screen.
|
||||
val offer = state.liveOffer(rawOffer)
|
||||
val handSummary = state.visibleHandSummary()
|
||||
val status = snap?.let(::tableStatus)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -96,7 +98,24 @@ fun TableScreen(vm: PokerViewModel) {
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 18.sp,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
text = status?.text.orEmpty(),
|
||||
color = when (status?.tone) {
|
||||
TableStatusTone.ACCENT -> Color(0xFFE3C179)
|
||||
TableStatusTone.NORMAL -> Color.White.copy(alpha = 0.78f)
|
||||
TableStatusTone.MUTED, null -> Color.White.copy(alpha = 0.48f)
|
||||
},
|
||||
fontWeight = if (status?.tone == TableStatusTone.ACCENT) {
|
||||
FontWeight.Bold
|
||||
} else {
|
||||
FontWeight.Normal
|
||||
},
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.height(24.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
val board = snap?.board.orEmpty()
|
||||
repeat(5) { i ->
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.jsjdesigns.poker
|
||||
|
||||
import com.jsjdesigns.poker.game.ActionType
|
||||
import com.jsjdesigns.poker.game.HandEvent
|
||||
import com.jsjdesigns.poker.game.Street
|
||||
import com.jsjdesigns.poker.game.TableSnapshot
|
||||
|
||||
enum class TableStatusTone { ACCENT, NORMAL, MUTED }
|
||||
|
||||
/** One short, snapshot-derived explanation of what the table is doing now. */
|
||||
data class TableStatus(val text: String, val tone: TableStatusTone)
|
||||
|
||||
/**
|
||||
* Turns an immutable engine frame into player-facing status copy.
|
||||
*
|
||||
* Phase transitions outrank [TableSnapshot.lastAction], because that event is
|
||||
* intentionally retained across the next board frame. Without this ordering the
|
||||
* freshly dealt flop would still announce the final pre-flop call.
|
||||
*/
|
||||
fun tableStatus(
|
||||
snapshot: TableSnapshot,
|
||||
heroSeat: Int = HERO_SEAT,
|
||||
): TableStatus? {
|
||||
if (snapshot.phase == TableSnapshot.Phase.SHOWDOWN ||
|
||||
snapshot.phase == TableSnapshot.Phase.COMPLETE
|
||||
) {
|
||||
return null // HandResultBar owns the terminal explanation.
|
||||
}
|
||||
|
||||
snapshot.toAct?.let { actingSeat ->
|
||||
// Carry the preceding action into the next decision frame so it remains
|
||||
// readable for more than the short post-action hold. Restrict it to this
|
||||
// street: the first actor on the flop must not resurrect a pre-flop call.
|
||||
val previous = snapshot.lastAction
|
||||
?.takeIf { it.street == snapshot.street }
|
||||
?.description(heroSeat)
|
||||
return if (actingSeat == heroSeat) {
|
||||
TableStatus(
|
||||
listOfNotNull("Your turn", previous).joinToString(" • "),
|
||||
TableStatusTone.ACCENT,
|
||||
)
|
||||
} else {
|
||||
val name = snapshot.seats.firstOrNull { it.index == actingSeat }?.name ?: "Player"
|
||||
TableStatus(
|
||||
listOfNotNull(previous, "$name is thinking…").joinToString(" • "),
|
||||
if (previous == null) TableStatusTone.MUTED else TableStatusTone.NORMAL,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return when (snapshot.phase) {
|
||||
TableSnapshot.Phase.DEALT ->
|
||||
TableStatus("Cards dealt", TableStatusTone.NORMAL)
|
||||
TableSnapshot.Phase.STREET_COMPLETE ->
|
||||
TableStatus("${snapshot.street.displayName()} dealt", TableStatusTone.NORMAL)
|
||||
TableSnapshot.Phase.BETTING ->
|
||||
snapshot.lastAction?.toStatus(heroSeat)
|
||||
?: TableStatus("Betting", TableStatusTone.MUTED)
|
||||
TableSnapshot.Phase.SHOWDOWN,
|
||||
TableSnapshot.Phase.COMPLETE,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun HandEvent.toStatus(heroSeat: Int): TableStatus {
|
||||
return TableStatus(description(heroSeat), TableStatusTone.NORMAL)
|
||||
}
|
||||
|
||||
private fun HandEvent.description(heroSeat: Int): String {
|
||||
val actor = if (seat == heroSeat) "You" else name
|
||||
val description = when (action.type) {
|
||||
ActionType.FOLD -> "folded"
|
||||
ActionType.CHECK -> "checked"
|
||||
ActionType.CALL -> "called ${action.amount}"
|
||||
ActionType.BET -> "bet ${action.amount}"
|
||||
ActionType.RAISE -> "raised to ${action.amount}"
|
||||
}
|
||||
return "$actor $description"
|
||||
}
|
||||
|
||||
private fun Street.displayName(): String = when (this) {
|
||||
Street.PREFLOP -> "Pre-flop"
|
||||
Street.FLOP -> "Flop"
|
||||
Street.TURN -> "Turn"
|
||||
Street.RIVER -> "River"
|
||||
}
|
||||
Reference in New Issue
Block a user