From d6e2543b9c7b43ce7e2b900356f31d8824cd1858 Mon Sep 17 00:00:00 2001 From: Markus Wagner Date: Thu, 14 Jan 2021 23:29:50 +0100 Subject: [PATCH] Add prototype for player resolveable events using Cellar --- src/main.rs | 89 ++++++++++++++++++++++++++++++++++- static/audio/chipsStack1.ogg | 3 ++ static/game.html | 90 +++++++++++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 static/audio/chipsStack1.ogg diff --git a/src/main.rs b/src/main.rs index c86714f..037b209 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ enum ClientMessage { DrawCard, Discard { index: usize }, TrashHand { index: usize }, + HandCardsChosen { choice: Vec }, } #[derive(Serialize)] @@ -60,6 +61,9 @@ enum ServerMessage { GameOver { score: Vec<(usize, u32)>, }, + ResolveRequest { + request: ResolveRequest, + }, } #[derive(Clone, Serialize)] @@ -165,11 +169,29 @@ impl Default for GameSetup { } } +/// Which player's input is requested to resolve the current effect +#[derive(Clone, Serialize)] +enum ResolvingPlayer { + ActivePlayer, +} + +/// Sent by the server to tell the client what information is requested +/// from the player to resolve the current effect +#[derive(Clone, Serialize)] +enum ResolveRequest { + ChooseHandCardsToDiscard { player: ResolvingPlayer }, +} + #[derive(Clone)] enum Effect { + /// Trigger effect if another card is played while effect is active + /// return true to indicate the event is resolved and should be removed OnCardPlayed(fn(&mut Game, &Card) -> bool), + Resolving(ResolveRequest, ResolvingEffect), } +type ResolvingEffect = fn(&mut Game, &ClientMessage); + pub struct Game { effects: Vec, players: Vec, @@ -180,6 +202,10 @@ pub struct Game { supply: Vec<(Card, usize)>, trash: Vec, turn_state: TurnState, + + /// Any effect from a card that requires further input from players + /// and blocks the game until fully resolved. + resolving_effect: Option<(ResolveRequest, ResolvingEffect)>, } impl Game { @@ -194,6 +220,7 @@ impl Game { supply: vec![], trash: vec![], turn_state: TurnState::default(), + resolving_effect: None, } } @@ -226,6 +253,23 @@ impl Game { cost: 2, types: vec![CardType::Action(|game| { action!(game, 1); + game.add_effect(Effect::Resolving( + ResolveRequest::ChooseHandCardsToDiscard { + player: ResolvingPlayer::ActivePlayer, + }, + |game, message| { + if let ClientMessage::HandCardsChosen { choice } = message { + let mut discarded = 0; + for c in choice.iter().sorted_by(|a, b| b.cmp(a)) { + game.players[game.active_player].discard(*c); + discarded += 1; + } + + game.players[game.active_player].draw(discarded); + game.resolving_effect = None; + } + }, + )); })], }, 10, @@ -429,6 +473,7 @@ impl Game { } let mut effects = self.effects.clone(); + self.effects.clear(); effects.retain(|effect| { match effect { Effect::OnCardPlayed(effect) => { @@ -436,11 +481,13 @@ impl Game { return false; } } + + Effect::Resolving(_, _) => {} } true }); - self.effects = effects; + self.effects.append(&mut effects); self.players[player_number].played_cards.push(card); true @@ -473,7 +520,15 @@ impl Game { } fn add_effect(&mut self, effect: Effect) { - self.effects.push(effect); + match effect { + Effect::OnCardPlayed(_) => { + self.effects.push(effect); + } + + Effect::Resolving(request, effect) => { + self.resolving_effect = Some((request, effect)); + } + } } } @@ -555,6 +610,20 @@ async fn broadcast_state(game: &Game) { }; broadcast(&game, &sm).await; } + + if let Some((request, _)) = &game.resolving_effect { + match request { + ResolveRequest::ChooseHandCardsToDiscard { ref player } => match player { + ResolvingPlayer::ActivePlayer => { + let p = game.players.get(game.active_player).unwrap(); + let sm = ServerMessage::ResolveRequest { + request: request.clone(), + }; + send_msg(&game, &p, &sm).await; + } + }, + } + } } async fn send_msg(game: &Game, player: &Player, sm: &ServerMessage) { @@ -847,6 +916,22 @@ async fn main() -> Result<(), std::io::Error> { .await; broadcast_state(&game).await; } + + ClientMessage::HandCardsChosen { .. } => { + let mut games = req.state().games.write().await; + let game = games.get_mut(&game_id).unwrap(); + + if player_number == game.active_player { + match game.resolving_effect { + Some((_, ref effect)) => { + effect(game, &msg); + } + None => {} + } + + broadcast_state(&game).await; + } + } } } diff --git a/static/audio/chipsStack1.ogg b/static/audio/chipsStack1.ogg new file mode 100644 index 0000000..89a4cd6 --- /dev/null +++ b/static/audio/chipsStack1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12bf175b9afcea112175b241ad3336c6244ac5fdcdff8b5942ecc03f2225e0c4 +size 6469 diff --git a/static/game.html b/static/game.html index 2102d95..3ce056c 100644 --- a/static/game.html +++ b/static/game.html @@ -81,6 +81,11 @@ img.card:hover { transition: transform 0.1s ease-in; } +.player-hand img.card.selected { + transform: translate(0px, -40px) scale(1.1) !important; + box-shadow: 0 0 10px lime; +} + .chat-window { border: 1px solid black; width: 300px; @@ -411,13 +416,71 @@ img.card:hover { width: 120px; } +.dialog { + position: absolute; + width: 500px; + border: 1px solid dimgray; + top: 50%; + z-index: 200; + background-color: rgba(1.0, 1.0, 1.0, 0.9); + left: 50%; + transform: translate(-50%, -50%); + box-shadow: 0 0 20px black; + display: grid; + grid-template-columns: auto auto; + color: white; + visibility: hidden; + grid-template-rows: 5fr auto; +} + +.dialog img { + grid-row-start: 1; + grid-row-end: 3; + margin: 5px; + width: 180px; + height: 288px; +} + +.dialog button { + grid-row-start: 2; + grid-column-start: 2; + margin: 10px auto; +} +
+ +

Choose any number of cards to discard.

+ +