From 9f9ce406ea3b363067a68e54d958fbaf6c3d5af2 Mon Sep 17 00:00:00 2001 From: Adam <24621027+adoyle0@users.noreply.github.com> Date: Mon, 26 Aug 2024 02:37:09 -0400 Subject: [PATCH] start rewriting game component --- client/src/components/cards.rs | 6 +- client/src/components/game.rs | 350 ++++++++++------------- client/src/components/game/header.rs | 14 + client/src/components/game/meta.rs | 17 ++ client/src/components/game/scoreboard.rs | 31 ++ client/src/components/game_old.rs | 211 ++++++++++++++ client/src/components/mod.rs | 2 +- lib/src/lib.rs | 2 +- 8 files changed, 434 insertions(+), 199 deletions(-) create mode 100644 client/src/components/game/header.rs create mode 100644 client/src/components/game/meta.rs create mode 100644 client/src/components/game/scoreboard.rs create mode 100644 client/src/components/game_old.rs diff --git a/client/src/components/cards.rs b/client/src/components/cards.rs index 7aaf1d3..266dd6d 100644 --- a/client/src/components/cards.rs +++ b/client/src/components/cards.rs @@ -2,11 +2,11 @@ use leptos::*; use lib::WhiteCardMeta; #[component] -pub fn BlackCard(card_data: ReadSignal<(String, u8)>) -> impl IntoView { +pub fn BlackCard(card_data: (String, u8)) -> impl IntoView { view! {
-

{move || card_data().0}

-

Pick: {move || card_data().1}

+

{card_data.0}

+

Pick: {card_data.1}

} } diff --git a/client/src/components/game.rs b/client/src/components/game.rs index 3342b16..6d5acf9 100644 --- a/client/src/components/game.rs +++ b/client/src/components/game.rs @@ -1,169 +1,105 @@ use crate::components::cards::*; +use crate::components::game::header::*; use crate::components::websocket::WebSocketContext; use leptos::*; use leptos_use::core::ConnectionReadyState; use lib::*; use serde_json::to_string; use std::collections::HashMap; +pub mod header; +pub mod meta; +pub mod scoreboard; #[component] pub fn Game() -> impl IntoView { + // Websocket stuff let websocket = expect_context::(); let tx = websocket.clone(); let (websocket_send, set_websocket_send) = create_signal("".to_string()); create_effect(move |_| { tx.send(&websocket_send()); }); + + // Incoming let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let game_meta = expect_context::>>(); let judge_round = expect_context::>>(); - let (game_id, set_game_id) = create_signal("".to_string()); - let (game_name, set_game_name) = create_signal("".to_string()); - let (game_host, set_game_host) = create_signal("".to_string()); - let (game_players, set_game_players) = create_signal(vec![]); - let (game_czar, set_game_czar) = create_signal("".to_string()); - let (game_white_count, set_game_white_count) = create_signal(0usize); - let (game_black_count, set_game_black_count) = create_signal(0usize); - let (game_white_discard_count, set_game_white_discard_count) = create_signal(0usize); - let (game_black, set_game_black) = create_signal(("".to_string(), 0u8)); - let (selected_cards_ordered, set_selected_cards_ordered) = create_signal::>(vec![]); - let (player_hand, set_player_hand) = create_signal::>(vec![]); - let (player_white, set_player_white) = - create_signal::>(HashMap::new()); + // Internal + let (selected_cards, set_selected_cards) = create_signal::>(vec![]); let (card_clicked, set_card_clicked) = create_signal::(String::new()); + let (white_map, set_white_map) = + create_signal::>(HashMap::new()); + + // For subcomponents provide_context::>(set_card_clicked); - // Clear everything on disconnect + // Put cards in a map for easier lookup + // The server sends a vec to preserve ordering create_effect(move |_| { - if !connected() { - set_game_id("".to_string()); - set_game_name("".to_string()); - set_game_host("".to_string()); - set_game_players(vec![]); - set_game_czar("".to_string()); - set_game_black(("".to_string(), 0u8)); - set_selected_cards_ordered(vec![]); - set_player_hand(vec![]); - set_player_white.update(|hand| { - hand.clear(); - }); - set_game_white_count(0usize); - set_game_black_count(0usize); - set_game_white_discard_count(0usize); - } - }); - - // Handle incoming judge message - create_effect(move |_| { - judge_round.with(move |judge_round| { - set_player_hand.update(|list| { - list.clear(); - }); - set_player_white.update(|list| { - list.clear(); - }); - set_card_clicked.update(|list| { - list.clear(); - }); - - // Load hand - if let Some(judge) = judge_round { - for cards in judge.cards_to_judge.clone() { - for card in cards { - set_player_white.update(|hand| { - hand.insert(card.uuid.clone(), card.clone()); - }); - set_player_hand.update(|hand| { - hand.push(card.uuid.clone()); - }); - } - } - } - }); - }); - - // Handle incoming state update - create_effect(move |_| { - if let Some(game) = game_meta() { - // Clear in case of (re-)joining game - set_card_clicked.update(|list| { - list.clear(); - }); - set_selected_cards_ordered.update(|list| { - list.clear(); - }); - set_player_hand.update(|list| { - list.clear(); - }); - set_player_white.update(|list| { - list.clear(); - }); - - // Load meta - set_game_id(game.uuid.clone()); - set_game_name(game.name.clone()); - set_game_host(game.host.clone()); - set_game_players(game.players.clone()); - set_game_czar(game.czar.clone()); - set_game_black(game.black.clone()); - set_game_white_count(game.white_count.clone()); - set_game_black_count(game.black_count.clone()); - set_game_white_discard_count(game.white_discard_count.clone()); - - // Load hand - for card in game.white { - set_player_white.update(|hand| { - hand.insert(card.uuid.clone(), card.clone()); - }); - set_player_hand.update(|hand| { - hand.push(card.uuid.clone()); + if game_meta().is_some() { + for card in game_meta().unwrap().white { + set_white_map.update(|map| { + map.insert(card.uuid.clone(), card.clone()); }); } } }); - // Move cards back and forth between hand and selected when clicked + // Toggle selected status of cards create_effect(move |_| { - if let Some(card_index) = player_hand() - .iter() - .position(|card| *card == card_clicked()) - { - set_player_hand.update(|list| { - list.remove(card_index); - }); + if card_clicked() != "".to_string() { + if !selected_cards().contains(white_map().get(&card_clicked()).unwrap()) { + set_selected_cards + .update(|cards| cards.push(white_map().get(&card_clicked()).unwrap().clone())) + } else if selected_cards().contains(white_map().get(&card_clicked()).unwrap()) { + set_selected_cards.update(|cards| { + cards.remove( + cards + .iter() + .position(|card| card == white_map().get(&card_clicked()).unwrap()) + .unwrap(), + ); + }) + } - set_selected_cards_ordered.update(|list| { - list.push(card_clicked()); - }) - } else if let Some(card_index) = selected_cards_ordered() - .iter() - .position(|card| *card == card_clicked()) - { - set_selected_cards_ordered.update(|list| { - list.remove(card_index); - }); - - set_player_hand.update(|list| { - list.push(card_clicked()); - }) + // You can't clear selected_cards without this line so don't fuck with it + set_card_clicked("".to_string()); } }); + // Runs when submit button is clicked let submit_move = move |_| { let msg = to_string(&PlayerMoveRequest { - game_id: game_id(), - card_ids: selected_cards_ordered(), + game_id: game_meta().unwrap().uuid.clone(), + card_ids: selected_cards() + .iter() + .map(|card| card.uuid.clone()) + .collect(), }) .unwrap(); set_websocket_send(msg); }; + // On incoming judge + create_effect(move |_| { + // Clear selected cards + if judge_round().is_some() { + set_selected_cards.update(|list| { + list.clear(); + }); + } + }); + + // Runs when judging submit button is clicked let submit_judge = move |_| { let msg = to_string(&JudgeDecisionRequest { - game_id: game_id(), - winning_cards: selected_cards_ordered(), + game_id: game_meta().unwrap().uuid.clone(), + winning_cards: selected_cards() + .iter() + .map(|card| card.uuid.clone()) + .collect(), }) .unwrap(); @@ -172,80 +108,106 @@ pub fn Game() -> impl IntoView { view! {
-
-

Game

- -

Name: {move || game_name()}

-

Host: {move || game_host()}

-

Czar: {move || game_czar()}

-

White Deck: {move || game_white_count().to_string()}

-

White Discard: {move || game_white_discard_count().to_string()}

-

Black Deck: {move || game_black_count().to_string()}

-
- -

Players:

- - - - - - - - {move || { - game_players() - .iter() - .map(|player| { - view! { - - - - +

Game

+ +
+ + + // //////////////////// + // Player is judging // + // //////////////////// + + +
+ + + // Selected cards + } + } + /> +
+ + {judge_round() + .unwrap() + .cards_to_judge + .iter() + .map(|group| view! { {format!("{:#?}", group)} }) + .collect_view()} + + + + // Submit button +
+ +
+
+ + // //////////////////// + // Player is playing // + // //////////////////// + + + + // Play cards +
+ + + // Selected cards + } + } + /> +
+ + // Submit button +
+ +
+ + // Player hand +
+ - -
+ }> + +
+ } + } + /> + + - // Game cards - // TODO: offset this correctly -
- - - // Card selection - } - } - /> -
-
- -
-
- -
- - // Player cards -
- } - } - /> - -
} } diff --git a/client/src/components/game/header.rs b/client/src/components/game/header.rs new file mode 100644 index 0000000..8788752 --- /dev/null +++ b/client/src/components/game/header.rs @@ -0,0 +1,14 @@ +use crate::components::game::meta::*; +use crate::components::game::scoreboard::*; +use leptos::*; +use lib::*; + +#[component] +pub fn Header(game_meta: GameStateMeta) -> impl IntoView { + view! { +
+ + +
+ } +} diff --git a/client/src/components/game/meta.rs b/client/src/components/game/meta.rs new file mode 100644 index 0000000..4781c57 --- /dev/null +++ b/client/src/components/game/meta.rs @@ -0,0 +1,17 @@ +use leptos::*; +use lib::*; + +#[component] +pub fn Meta(game_meta: GameStateMeta) -> impl IntoView { + view! { + +

Name: {game_meta.name}

+

Host: {game_meta.host}

+

Czar: {game_meta.czar}

+

White Deck: {game_meta.white_count.to_string()}

+

White Discard: {move || game_meta.white_discard_count.to_string()}

+

Black Deck: {move || game_meta.black_count.to_string()}

+
+ } +} + diff --git a/client/src/components/game/scoreboard.rs b/client/src/components/game/scoreboard.rs new file mode 100644 index 0000000..6c48632 --- /dev/null +++ b/client/src/components/game/scoreboard.rs @@ -0,0 +1,31 @@ +use leptos::*; +use lib::*; + +#[component] +pub fn Scoreboard(game_meta: GameStateMeta) -> impl IntoView { + view! { + +

Players:

+
NameScore
{&player.name} - {&player.score.to_string()} -
+ + + + + + + {game_meta + .players + .iter() + .map(|player| { + view! { + + + + + } + }) + .collect_view()} +
NameScore
{&player.name}{&player.score.to_string()}
+
+ } +} diff --git a/client/src/components/game_old.rs b/client/src/components/game_old.rs new file mode 100644 index 0000000..9cc027d --- /dev/null +++ b/client/src/components/game_old.rs @@ -0,0 +1,211 @@ +use crate::components::cards::*; +use crate::components::websocket::WebSocketContext; +use leptos::*; +use leptos_use::core::ConnectionReadyState; +use lib::*; +use serde_json::to_string; +use std::collections::HashMap; + +#[component] +pub fn Game() -> impl IntoView { + let websocket = expect_context::(); + let tx = websocket.clone(); + let (websocket_send, set_websocket_send) = create_signal("".to_string()); + create_effect(move |_| { + tx.send(&websocket_send()); + }); + let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; + let game_meta = expect_context::>>(); + let judge_round = expect_context::>>(); + + let (game_id, set_game_id) = create_signal("".to_string()); + let (game_name, set_game_name) = create_signal("".to_string()); + let (game_host, set_game_host) = create_signal("".to_string()); + let (game_players, set_game_players) = create_signal(vec![]); + let (game_czar, set_game_czar) = create_signal("".to_string()); + let (game_white_count, set_game_white_count) = create_signal(0usize); + let (game_black_count, set_game_black_count) = create_signal(0usize); + let (game_white_discard_count, set_game_white_discard_count) = create_signal(0usize); + let (game_black, set_game_black) = create_signal(("".to_string(), 0u8)); + let (selected_cards_ordered, set_selected_cards_ordered) = create_signal::>(vec![]); + let (player_hand, set_player_hand) = create_signal::>(vec![]); + let (player_white, set_player_white) = + create_signal::>(HashMap::new()); + let (card_clicked, set_card_clicked) = create_signal::(String::new()); + provide_context::>(set_card_clicked); + + // Clear everything on disconnect + create_effect(move |_| { + if !connected() { + set_game_id("".to_string()); + set_game_name("".to_string()); + set_game_host("".to_string()); + set_game_players(vec![]); + set_game_czar("".to_string()); + set_game_black(("".to_string(), 0u8)); + set_selected_cards_ordered(vec![]); + set_player_hand(vec![]); + set_player_white.update(|hand| { + hand.clear(); + }); + set_game_white_count(0usize); + set_game_black_count(0usize); + set_game_white_discard_count(0usize); + } + }); + + // Handle incoming judge message + create_effect(move |_| { + judge_round.with(move |judge_round| { + set_player_hand.update(|list| { + list.clear(); + }); + set_player_white.update(|list| { + list.clear(); + }); + set_card_clicked.update(|list| { + list.clear(); + }); + + // Load hand + if let Some(judge) = judge_round { + for cards in judge.cards_to_judge.clone() { + for card in cards { + set_player_white.update(|hand| { + hand.insert(card.uuid.clone(), card.clone()); + }); + set_player_hand.update(|hand| { + hand.push(card.uuid.clone()); + }); + } + } + } + }); + }); + + // Handle incoming state update + create_effect(move |_| { + if let Some(game) = game_meta() { + // Clear in case of (re-)joining game + set_card_clicked.update(|list| { + list.clear(); + }); + set_selected_cards_ordered.update(|list| { + list.clear(); + }); + set_player_hand.update(|list| { + list.clear(); + }); + set_player_white.update(|list| { + list.clear(); + }); + + // Load meta + set_game_id(game.uuid.clone()); + set_game_name(game.name.clone()); + set_game_host(game.host.clone()); + set_game_players(game.players.clone()); + set_game_czar(game.czar.clone()); + set_game_black(game.black.clone()); + set_game_white_count(game.white_count.clone()); + set_game_black_count(game.black_count.clone()); + set_game_white_discard_count(game.white_discard_count.clone()); + + // Load hand + for card in game.white { + set_player_white.update(|hand| { + hand.insert(card.uuid.clone(), card.clone()); + }); + set_player_hand.update(|hand| { + hand.push(card.uuid.clone()); + }); + } + } + }); + + // Move cards back and forth between hand and selected when clicked + create_effect(move |_| { + if let Some(card_index) = player_hand() + .iter() + .position(|card| *card == card_clicked()) + { + set_player_hand.update(|list| { + list.remove(card_index); + }); + + set_selected_cards_ordered.update(|list| { + list.push(card_clicked()); + }) + } else if let Some(card_index) = selected_cards_ordered() + .iter() + .position(|card| *card == card_clicked()) + { + set_selected_cards_ordered.update(|list| { + list.remove(card_index); + }); + + set_player_hand.update(|list| { + list.push(card_clicked()); + }) + } + }); + + let submit_move = move |_| { + let msg = to_string(&PlayerMoveRequest { + game_id: game_id(), + card_ids: selected_cards_ordered(), + }) + .unwrap(); + + set_websocket_send(msg); + }; + + let submit_judge = move |_| { + let msg = to_string(&JudgeDecisionRequest { + game_id: game_id(), + winning_cards: selected_cards_ordered(), + }) + .unwrap(); + + set_websocket_send(msg); + }; + + view! { +
+ // Game cards + // TODO: offset this correctly +
+ // Card selection + } + } + /> +
+
+ +
+
+ +
+ + // Player cards +
+ } + } + /> + +
+
+ } +} diff --git a/client/src/components/mod.rs b/client/src/components/mod.rs index a994774..4f663e7 100644 --- a/client/src/components/mod.rs +++ b/client/src/components/mod.rs @@ -1,6 +1,6 @@ pub mod auth; -pub mod cards; pub mod browser; +pub mod cards; pub mod chat; pub mod debug; pub mod game; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 6268cca..ed73e4b 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -33,7 +33,7 @@ pub struct PlayerMoveRequest { } /// White Card Meta -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct WhiteCardMeta { pub uuid: String, pub text: String,