summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt3
-rw-r--r--sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/Moves.kt3
-rw-r--r--sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt3
-rw-r--r--sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt2
-rw-r--r--sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt40
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt72
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt3
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt25
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameSagas.kt3
9 files changed, 96 insertions, 58 deletions
diff --git a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
index 84176e03..d95c32f5 100644
--- a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
+++ b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
@@ -113,9 +113,6 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat
suspend fun watchPlayerReady(gameId: Long): StompSubscription<String> =
stompSession.subscribe("/topic/game/$gameId/playerReady", String.serializer())
- suspend fun watchTableUpdates(gameId: Long): StompSubscription<GameState> =
- stompSession.subscribe("/topic/game/$gameId/tableUpdates", GameState.serializer())
-
suspend fun watchPreparedCards(gameId: Long): StompSubscription<PreparedCard> =
stompSession.subscribe("/topic/game/$gameId/prepared", PreparedCard.serializer())
diff --git a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/Moves.kt b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/Moves.kt
index 05b95b17..22cbfc8c 100644
--- a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/Moves.kt
+++ b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/Moves.kt
@@ -8,6 +8,7 @@ import org.luxons.sevenwonders.model.resources.noTransactions
import org.luxons.sevenwonders.model.wonders.WonderBuildability
enum class Action(val message: String) {
+ SAY_READY("Say when you're ready to get your next hand"),
PLAY("Pick the card you want to play or discard."),
PLAY_2("Pick the first card you want to play or discard. Note that you have the ability to play these 2 last cards. You will choose how to play the last one during your next turn."),
PLAY_LAST("You have the special ability to play your last card. Choose how you want to play it."),
@@ -20,7 +21,7 @@ data class PlayerTurnInfo(
val playerIndex: Int,
val table: GameState,
val action: Action,
- val hand: List<HandCard>,
+ val hand: List<HandCard>?,
val preparedMove: PlayedMove?,
val neighbourGuildCards: List<TableCard>
) {
diff --git a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt
index aca9b140..d28c8ca3 100644
--- a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt
+++ b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt
@@ -31,5 +31,6 @@ data class PlayerDTO(
val displayName: String,
val index: Int,
val isGameOwner: Boolean,
- val isMe: Boolean
+ val isMe: Boolean,
+ val isReady: Boolean
)
diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt
index 5fb73b23..a2390a7d 100644
--- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt
+++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt
@@ -13,4 +13,4 @@ fun Lobby.toDTO(currentPlayer: Player): LobbyDTO {
}
fun Player.toDTO(currentUser: String) =
- PlayerDTO(username, displayName, index, isGameOwner, username === currentUser)
+ PlayerDTO(username, displayName, index, isGameOwner, username === currentUser, isReady)
diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt
index e1fae39b..0db7a21c 100644
--- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt
+++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt
@@ -2,7 +2,8 @@ package org.luxons.sevenwonders.server.controllers
import org.hildan.livedoc.core.annotations.Api
import org.luxons.sevenwonders.engine.Game
-import org.luxons.sevenwonders.model.GameState
+import org.luxons.sevenwonders.model.Action
+import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.actions.PrepareMoveAction
import org.luxons.sevenwonders.model.cards.PreparedCard
import org.luxons.sevenwonders.server.api.toDTO
@@ -40,8 +41,7 @@ class GameController @Autowired constructor(
val game = player.game
logger.info("Game {}: player {} is ready for the next turn", game.id, player)
- val lobby = player.lobby
- val players = lobby.getPlayers()
+ val players = player.lobby.getPlayers()
sendPlayerReady(game.id, player)
@@ -49,20 +49,10 @@ class GameController @Autowired constructor(
if (allReady) {
logger.info("Game {}: all players ready, sending turn info", game.id)
players.forEach { it.isReady = false }
- sendTurnInfo(players, game)
+ sendTurnInfo(players, game, false)
}
}
- private fun sendTurnInfo(players: List<Player>, game: Game) {
- for (turnInfo in game.getCurrentTurnInfo()) {
- val player = players[turnInfo.playerIndex]
- template.convertAndSendToUser(player.username, "/queue/game/turn", turnInfo)
- }
- }
-
- private fun sendPlayerReady(gameId: Long, player: Player) =
- template.convertAndSend("/topic/game/$gameId/playerReady", "\"${player.username}\"")
-
/**
* Prepares the player's next move. When all players have prepared their moves, all moves are executed.
*
@@ -82,16 +72,28 @@ class GameController @Autowired constructor(
if (game.allPlayersPreparedTheirMove()) {
logger.info("Game {}: all players have prepared their move, executing turn...", game.id)
- val table = game.playTurn()
- sendPlayedMoves(game.id, table)
+ game.playTurn()
+ sendTurnInfo(player.lobby.getPlayers(), game, true)
}
}
- private fun sendPlayedMoves(gameId: Long, gameState: GameState) =
- template.convertAndSend("/topic/game/$gameId/tableUpdates", gameState)
+ private fun sendPlayerReady(gameId: Long, player: Player) =
+ template.convertAndSend("/topic/game/$gameId/playerReady", "\"${player.username}\"")
private fun sendPreparedCard(gameId: Long, preparedCard: PreparedCard) =
- template.convertAndSend("/topic/game/$gameId/prepared", preparedCard)
+ template.convertAndSend("/topic/game/$gameId/prepared", preparedCard)
+
+ private fun sendTurnInfo(players: List<Player>, game: Game, hideHands: Boolean) {
+ val turns = game.getCurrentTurnInfo()
+ val turnsToSend = if (hideHands) turns.hideHandsAndWaitForReadiness() else turns
+ for (turnInfo in turnsToSend) {
+ val player = players[turnInfo.playerIndex]
+ template.convertAndSendToUser(player.username, "/queue/game/turn", turnInfo)
+ }
+ }
+
+ private fun Collection<PlayerTurnInfo>.hideHandsAndWaitForReadiness() =
+ map { it.copy(action = Action.SAY_READY, hand = null) }
companion object {
private val logger = LoggerFactory.getLogger(GameController::class.java)
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
index 11ce09c1..d9de85d9 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
@@ -2,6 +2,7 @@ package org.luxons.sevenwonders.ui.components.game
import com.palantir.blueprintjs.Intent
import com.palantir.blueprintjs.bpButton
+import com.palantir.blueprintjs.bpButtonGroup
import com.palantir.blueprintjs.bpNonIdealState
import com.palantir.blueprintjs.org.luxons.sevenwonders.ui.components.game.handComponent
import kotlinx.css.CSSBuilder
@@ -12,10 +13,16 @@ import kotlinx.css.backgroundSize
import kotlinx.css.bottom
import kotlinx.css.left
import kotlinx.css.overflow
+import kotlinx.css.pct
import kotlinx.css.position
+import kotlinx.css.properties.transform
+import kotlinx.css.properties.translate
import kotlinx.css.px
+import kotlinx.css.rem
import kotlinx.css.right
import kotlinx.css.top
+import kotlinx.html.DIV
+import org.luxons.sevenwonders.model.Action
import org.luxons.sevenwonders.model.PlayerMove
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.PlayerDTO
@@ -28,11 +35,14 @@ import react.RClass
import react.RComponent
import react.RProps
import react.RState
+import react.ReactElement
import react.dom.*
+import styled.StyledDOMBuilder
import styled.css
import styled.styledDiv
interface GameSceneStateProps: RProps {
+ var playerIsReady: Boolean
var players: List<PlayerDTO>
var turnInfo: PlayerTurnInfo?
}
@@ -55,45 +65,76 @@ private class GameScene(props: GameSceneProps) : RComponent<GameSceneProps, RSta
}
val turnInfo = props.turnInfo
if (turnInfo == null) {
- gamePreStart(props.sayReady)
+ gamePreStart()
} else {
turnInfoScene(turnInfo)
}
}
}
- private fun RBuilder.gamePreStart(onReadyClicked: () -> Unit) {
+ private fun RBuilder.gamePreStart() {
bpNonIdealState(
description = createElement {
p { +"Click 'ready' when you are"}
},
action = createElement {
+ sayReadyButton()
+ }
+ )
+ }
+
+ private fun RBuilder.sayReadyButton(block: StyledDOMBuilder<DIV>.() -> Unit = {}): ReactElement {
+ val isReady = props.playerIsReady
+ val intent = if (isReady) Intent.SUCCESS else Intent.PRIMARY
+ return styledDiv {
+ bpButtonGroup {
bpButton(
large = true,
- intent = Intent.PRIMARY,
- icon = "play",
- onClick = { onReadyClicked() }
+ disabled = isReady,
+ intent = intent,
+ icon = if (isReady) "tick-circle" else "play",
+ onClick = { props.sayReady() }
) {
+"READY"
}
+ bpButton(
+ large = true,
+ icon = "people",
+ disabled = isReady,
+ intent = intent
+ ) {
+ +"${props.players.count { it.isReady }}/${props.players.size}"
+ }
}
- )
+ block()
+ }
}
private fun RBuilder.turnInfoScene(turnInfo: PlayerTurnInfo) {
val board = turnInfo.table.boards[turnInfo.playerIndex]
div {
+ // TODO use blueprint's Callout component without header and primary intent
p { + turnInfo.message }
boardComponent(board = board)
- handComponent(
- cards = turnInfo.hand,
- wonderUpgradable = turnInfo.wonderBuildability.isBuildable,
- prepareMove = props.prepareMove
- )
- productionBar(
- gold = board.gold,
- production = board.production
- )
+ val hand = turnInfo.hand
+ if (hand != null) {
+ handComponent(
+ cards = hand,
+ wonderUpgradable = turnInfo.wonderBuildability.isBuildable,
+ prepareMove = props.prepareMove
+ )
+ }
+ if (turnInfo.action == Action.SAY_READY) {
+ sayReadyButton {
+ css {
+ position = Position.absolute
+ bottom = 4.rem
+ left = 50.pct
+ transform { translate(tx = (-50).pct) }
+ }
+ }
+ }
+ productionBar(gold = board.gold, production = board.production)
}
}
}
@@ -108,6 +149,7 @@ private val gameScene: RClass<GameSceneProps> = connectStateAndDispatch<GameScen
sayReady = { dispatch(RequestSayReady()) }
},
mapStateToProps = { state, _ ->
+ playerIsReady = state.currentPlayer?.isReady == true
players = state.currentLobby?.players ?: emptyList()
turnInfo = state.currentTurnInfo
}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
index 223cd5c1..37238e0a 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
@@ -1,6 +1,5 @@
package org.luxons.sevenwonders.ui.redux
-import org.luxons.sevenwonders.model.GameState
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.api.PlayerDTO
@@ -24,5 +23,3 @@ data class TurnInfoEvent(val turnInfo: PlayerTurnInfo): RAction
data class PreparedCardEvent(val card: PreparedCard): RAction
data class PlayerReadyEvent(val username: String): RAction
-
-data class TableUpdateEvent(val table: GameState): RAction
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
index 28bb7993..507a3941 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
@@ -1,6 +1,5 @@
package org.luxons.sevenwonders.ui.redux
-import org.luxons.sevenwonders.model.GameState
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.api.PlayerDTO
@@ -11,8 +10,7 @@ data class SwState(
val gamesById: Map<Long, LobbyDTO> = emptyMap(),
val currentPlayerUsername: String? = null,
val currentLobbyId: Long? = null,
- val currentTurnInfo: PlayerTurnInfo? = null,
- val currentTable: GameState? = null
+ val currentTurnInfo: PlayerTurnInfo? = null
) {
val games: List<LobbyDTO> = gamesById.values.toList()
val currentLobby: LobbyDTO? = currentLobbyId?.let { gamesById[it] }
@@ -24,14 +22,15 @@ fun rootReducer(state: SwState, action: RAction): SwState = state.copy(
gamesById = gamesReducer(state.gamesById, action),
currentPlayerUsername = currentPlayerReducer(state.currentPlayerUsername, action),
currentLobbyId = currentLobbyReducer(state.currentLobbyId, action),
- currentTurnInfo = currentTurnInfoReducer(state.currentTurnInfo, action),
- currentTable = currentTableReducer(state.currentTable, action)
+ currentTurnInfo = currentTurnInfoReducer(state.currentTurnInfo, action)
)
private fun playersReducer(playersByUsername: Map<String, PlayerDTO>, action: RAction): Map<String, PlayerDTO> = when (action) {
is UpdatePlayers -> playersByUsername + action.players
is UpdateLobbyAction -> playersByUsername + action.lobby.players.associateBy { it.username }
is SetCurrentPlayerAction -> playersByUsername + (action.player.username to action.player)
+ is PlayerReadyEvent -> playersByUsername + (action.username to playersByUsername.getValue(action.username)
+ .copy(isReady = true))
else -> playersByUsername
}
@@ -39,6 +38,15 @@ private fun gamesReducer(games: Map<Long, LobbyDTO>, action: RAction): Map<Long,
is UpdateGameListAction -> action.games.associateBy { it.id } // replaces because should remove deleted games
is EnterLobbyAction -> games + (action.lobby.id to action.lobby)
is UpdateLobbyAction -> games + (action.lobby.id to action.lobby)
+ is PlayerReadyEvent -> games.mapValues { (_, l) ->
+ if (l.players.any { it.username == action.username }) {
+ l.copy(players = l.players.map { p ->
+ if (p.username == action.username) p.copy(isReady = true) else p
+ })
+ } else {
+ l
+ }
+ }
else -> games
}
@@ -54,12 +62,5 @@ private fun currentLobbyReducer(currentLobbyId: Long?, action: RAction): Long? =
private fun currentTurnInfoReducer(currentTurnInfo: PlayerTurnInfo?, action: RAction): PlayerTurnInfo? = when (action) {
is TurnInfoEvent -> action.turnInfo
- is TableUpdateEvent -> null
else -> currentTurnInfo
}
-
-private fun currentTableReducer(currentTable: GameState?, action: RAction): GameState? = when (action) {
- is TurnInfoEvent -> action.turnInfo.table
- is TableUpdateEvent -> action.table
- else -> currentTable
-}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameSagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameSagas.kt
index e3675799..cadf56e9 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameSagas.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameSagas.kt
@@ -7,7 +7,6 @@ import org.luxons.sevenwonders.ui.redux.PlayerReadyEvent
import org.luxons.sevenwonders.ui.redux.PreparedCardEvent
import org.luxons.sevenwonders.ui.redux.RequestPrepareMove
import org.luxons.sevenwonders.ui.redux.RequestSayReady
-import org.luxons.sevenwonders.ui.redux.TableUpdateEvent
import org.luxons.sevenwonders.ui.redux.TurnInfoEvent
suspend fun SwSagaContext.gameSaga(session: SevenWondersSession) {
@@ -15,13 +14,11 @@ suspend fun SwSagaContext.gameSaga(session: SevenWondersSession) {
coroutineScope {
val playerReadySub = session.watchPlayerReady(lobby.id)
val preparedCardsSub = session.watchPreparedCards(lobby.id)
- val tableUpdatesSub = session.watchTableUpdates(lobby.id)
val turnInfoSub = session.watchTurns()
val sayReadyJob = launch { onEach<RequestSayReady> { session.sayReady() } }
val prepareMoveJob = launch { onEach<RequestPrepareMove> { session.prepareMove(it.move) } }
launch { dispatchAll(playerReadySub.messages) { PlayerReadyEvent(it) } }
launch { dispatchAll(preparedCardsSub.messages) { PreparedCardEvent(it) } }
- launch { dispatchAll(tableUpdatesSub.messages) { TableUpdateEvent(it) } }
launch { dispatchAll(turnInfoSub.messages) { TurnInfoEvent(it) } }
// TODO await game end
// TODO unsubscribe all subs, cancel all jobs
bgstack15