Katan: Cities & Knights docs, knight menu for any own knight, tidier player card
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_016eTor2tpxKb4dXbd8RqnvZ
This commit is contained in:
@@ -360,7 +360,7 @@ object GameCatalog {
|
||||
minPlayers = 3,
|
||||
maxPlayers = 4,
|
||||
teamBased = false,
|
||||
blurb = "Trade, build, and settle the island.",
|
||||
blurb = "Trade, build, and settle the island, in the base game or Cities & Knights.",
|
||||
// Engine and screen exist but have not been verified on two real
|
||||
// devices yet. Dev builds can open it as a *Preview*; a production
|
||||
// build does not list it. Flip `available` on, and `devPreview`
|
||||
|
||||
@@ -71,11 +71,14 @@ data class BoardTargets(
|
||||
val edges: Set<Int> = emptySet(),
|
||||
val hexes: Set<Int> = emptySet(),
|
||||
val knights: Set<Int> = emptySet(),
|
||||
/** The player's other knights: tappable for their menu, but with nothing to do, so not ringed. */
|
||||
val idleKnights: Set<Int> = emptySet(),
|
||||
) {
|
||||
val isEmpty: Boolean get() = vertices.isEmpty() && edges.isEmpty() && hexes.isEmpty() && knights.isEmpty()
|
||||
val isEmpty: Boolean
|
||||
get() = vertices.isEmpty() && edges.isEmpty() && hexes.isEmpty() && knights.isEmpty() && idleKnights.isEmpty()
|
||||
|
||||
/** Every crossing a tap can land on. */
|
||||
val tappableVertices: Set<Int> get() = vertices + knights
|
||||
val tappableVertices: Set<Int> get() = vertices + knights + idleKnights
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,6 +146,8 @@ object CatanInteraction {
|
||||
setupVertex: Int?,
|
||||
movingKnight: Int? = null,
|
||||
armed: ArmedCard? = null,
|
||||
/** The local seat, so its sleeping knights can be tapped for their menu too. */
|
||||
localSeat: Int? = null,
|
||||
): BoardTargets {
|
||||
val setup = legal.filterIsInstance<SetupPlace>()
|
||||
if (setup.isNotEmpty()) {
|
||||
@@ -187,7 +192,15 @@ object CatanInteraction {
|
||||
BuildKind.KNIGHT ->
|
||||
BoardTargets(vertices = legal.filterIsInstance<HireKnight>().map { it.vertex }.toSet())
|
||||
|
||||
null -> BoardTargets(knights = knightsWithActions(legal))
|
||||
null -> {
|
||||
val ready = knightsWithActions(legal)
|
||||
val mine = if (localSeat != null && legal.isNotEmpty()) {
|
||||
state.ck?.knights?.filter { it.owner == localSeat }?.map { it.vertex }?.toSet().orEmpty()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
BoardTargets(knights = ready, idleKnights = mine - ready)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +336,8 @@ object CatanInteraction {
|
||||
setupVertex: Int?,
|
||||
movingKnight: Int? = null,
|
||||
armed: ArmedCard? = null,
|
||||
/** The local player's knights, which can be tapped for their menu even with nothing to do. */
|
||||
ownKnights: Set<Int> = emptySet(),
|
||||
): TapOutcome {
|
||||
val setup = legal.filterIsInstance<SetupPlace>()
|
||||
if (setup.isNotEmpty()) {
|
||||
@@ -350,7 +365,7 @@ object CatanInteraction {
|
||||
BuildKind.CITY -> BuildCity(vertex)
|
||||
BuildKind.WALL -> BuildWall(vertex)
|
||||
BuildKind.KNIGHT -> HireKnight(vertex)
|
||||
else -> return if (building == null && vertex in knightsWithActions(legal)) {
|
||||
else -> return if (building == null && (vertex in knightsWithActions(legal) || vertex in ownKnights)) {
|
||||
TapOutcome.PickKnight(vertex)
|
||||
} else {
|
||||
TapOutcome.Ignored
|
||||
|
||||
@@ -239,7 +239,7 @@ fun CatanPlayerCard(
|
||||
} else {
|
||||
CardLine("$cards cards · ${ck.progressCounts[seat]} progress", MaterialTheme.typography.bodySmall)
|
||||
CardLine(
|
||||
"Knights ${CatanCk.knightsOf(state, seat).size} (strength $strength) · " +
|
||||
"⚔ ${CatanCk.knightsOf(state, seat).size} · str $strength · " +
|
||||
"Road ${CatanRules.longestRoadFor(state, seat)}",
|
||||
MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
|
||||
@@ -117,7 +117,6 @@ fun CatanScreen(
|
||||
setupVertex = null
|
||||
}
|
||||
if (movingKnight != null && legal.none { it is MoveKnight && it.from == movingKnight }) movingKnight = null
|
||||
if (knightMenu != null && CatanInteraction.knightActions(legal, knightMenu!!).isEmpty()) knightMenu = null
|
||||
armed?.let { card -> if (card.plays.none { it in legal }) armed = null }
|
||||
if (legal.isEmpty()) {
|
||||
showCards = false
|
||||
@@ -159,7 +158,8 @@ fun CatanScreen(
|
||||
return
|
||||
}
|
||||
|
||||
val targets = CatanInteraction.targets(current, legal, building, setupVertex, movingKnight, armed)
|
||||
val targets = CatanInteraction.targets(current, legal, building, setupVertex, movingKnight, armed, localSeat)
|
||||
val ownKnights = current.ck?.knights?.filter { it.owner == localSeat }?.map { it.vertex }?.toSet().orEmpty()
|
||||
val devHand = CatanInteraction.devHand(current, localSeat, legal)
|
||||
val progressHand = CatanInteraction.progressHand(current, localSeat, legal)
|
||||
val playing = current.phase == CatanPhase.PLAYING && legal.isNotEmpty()
|
||||
@@ -219,7 +219,12 @@ fun CatanScreen(
|
||||
targets = targets,
|
||||
selectedVertex = setupVertex,
|
||||
onVertexTap = {
|
||||
handle(CatanInteraction.onVertexTap(it, legal, building, setupVertex, movingKnight, armed))
|
||||
handle(
|
||||
CatanInteraction.onVertexTap(
|
||||
it, legal, building, setupVertex, movingKnight, armed,
|
||||
ownKnights = if (legal.isNotEmpty()) ownKnights else emptySet(),
|
||||
),
|
||||
)
|
||||
},
|
||||
onEdgeTap = { handle(CatanInteraction.onEdgeTap(it, legal, building, setupVertex, armed)) },
|
||||
onHexTap = { handle(CatanInteraction.onHexTap(it, legal, armed)) },
|
||||
|
||||
@@ -58,8 +58,8 @@ data class CatanStyle(
|
||||
/** Wall round a city, and the barbarian strip. */
|
||||
val wall: Color = Color(0xFF5D4037),
|
||||
val barbarian: Color = Color(0xFFB71C1C),
|
||||
/** A knight that is awake and ready to act. */
|
||||
val knightReady: Color = Color(0xFFFFEB3B),
|
||||
/** The halo round a knight that is awake. White, so it is not mistaken for the yellow tap ring. */
|
||||
val knightReady: Color = Color(0xFFFFFFFF),
|
||||
|
||||
// --- Players ------------------------------------------------------------
|
||||
/** One colour per seat, in seat order. Roads, settlements, cities, player strip. */
|
||||
|
||||
Reference in New Issue
Block a user