M client/src/api/game/mod.rs => client/src/api/game/mod.rs +5 -3
@@ 31,7 31,7 @@ pub use create_action::*;
mod details;
pub use details::*;
-#[derive(Deserialize, Serialize, Clone)]
+#[derive(Deserialize, Serialize, Clone, PartialEq)]
pub enum Command {
InitSupplyPile {
card: Card,
@@ 50,15 50,17 @@ pub enum Command {
Discard {
index: usize,
},
- /// end the targets turn
+ /// end the invokers turn, and invoke StartTurn for the target
EndTurn {},
+ /// start the targets turn
+ StartTurn {},
/// change player state to another
ChangePlayerState {
state: PlayerState,
},
}
-#[derive(Deserialize, Serialize, Clone)]
+#[derive(Deserialize, Serialize, Clone, PartialEq)]
pub struct Action {
pub id: String,
pub created_at: chrono::DateTime<chrono::Utc>,
M client/src/game_status/mod.rs => client/src/game_status/mod.rs +2 -2
@@ 14,14 14,14 @@ use crate::api::game::{Action, Command, Game};
mod parser;
-#[derive(Deserialize, Serialize, Clone)]
+#[derive(Deserialize, Serialize, Clone, PartialEq)]
pub struct CardAction {
pub target: String,
pub command: Command,
pub seed: u64,
}
-#[derive(Deserialize, Serialize, Clone)]
+#[derive(Deserialize, Serialize, Clone, PartialEq)]
pub struct Card {
pub name: String,
/// short details shown on the card, e.g. Draw 2
M client/src/game_status/parser.rs => client/src/game_status/parser.rs +87 -68
@@ 11,8 11,9 @@ use std::collections::HashMap;
use fastrand::Rng;
use crate::{
- api::game::{Command, Game},
+ api::game::{Action, Command, Game},
game_status::SupplyPile,
+ seed_gen,
};
use super::{GameStatus, PlayerState, PlayerStatus};
@@ 80,80 81,98 @@ pub fn parse(game: &Game) -> Result<GameStatus, ()> {
},
);
- // TODO: a system for reparsing if needed, e.g. after something
- // modifies the actions Vector.
- for action in &game_status.actions {
- // invoker: the one who invoked the action
- // target: the one who the action affects, may also be the invoker, e.g. draw
- let (invoker, target) =
- get_invoker_target(&mut game_status.players, &action.invoker, &action.target);
-
- match &action.command {
- Command::InitSupplyPile { card, amount } => {
- let pile = SupplyPile {
- card: card.clone(),
- amount: *amount,
- };
-
- game_status.supply_piles.push(pile);
- }
- Command::TakeFromPile { index, for_cost } => {
- // index should be within range
- assert!(*index <= game_status.supply_piles.len());
- let pile = &mut game_status
- .supply_piles
- .get_mut(*index)
- .unwrap_or_else(|| unreachable!());
-
- // pile should not be empty
- assert!(pile.amount > 0);
- pile.amount -= 1;
-
- // player should have enough
- assert!(*for_cost <= target.currency);
- target.currency -= for_cost;
-
- target.discard.push(pile.card.clone());
- }
- Command::Draw { amount } => {
- for _ in 0..*amount {
- if target.deck.is_empty() {
- shuffle_discard_to_deck(target, action.seed.parse::<u64>().unwrap());
- }
-
- target
- .hand
- .push(target.deck.pop().unwrap_or_else(|| unreachable!()));
- }
- }
- Command::Discard { index } => {
- // index should be within range
- assert!(*index <= target.hand.len());
- target.discard.push(target.hand.remove(*index));
- }
- Command::EndTurn {} => {
- // NOTE: target will be the next player
+ for action in game_status.actions.clone() {
+ parse_action(&action, game, &mut game_status);
+ }
- // set player to idle
- invoker.state = PlayerState::Idle;
+ Ok(game_status)
+}
- // set the target to the play phase
- target.state = PlayerState::PlayPhase;
+fn parse_action(action: &Action, game: &Game, game_status: &mut GameStatus) {
+ // invoker: the one who invoked the action
+ // target: the one who the action affects, may also be the invoker, e.g. draw
+ let (invoker, target) =
+ get_invoker_target(&mut game_status.players, &action.invoker, &action.target);
- // clear currency
- invoker.currency = 0;
+ let Some(action_pos) = game_status.actions.iter().position(|a| *a == action.clone()) else {
+ panic!("Action was not found in game_status.actions!");
+ };
- // other?
- }
- Command::ChangePlayerState { state } => {
- target.state = *state;
+ match &action.command {
+ Command::InitSupplyPile { card, amount } => {
+ let pile = SupplyPile {
+ card: card.clone(),
+ amount: *amount,
+ };
+
+ game_status.supply_piles.push(pile);
+ }
+ Command::TakeFromPile { index, for_cost } => {
+ // index should be within range
+ assert!(*index <= game_status.supply_piles.len());
+ let pile = &mut game_status
+ .supply_piles
+ .get_mut(*index)
+ .unwrap_or_else(|| unreachable!());
+
+ // pile should not be empty
+ assert!(pile.amount > 0);
+ pile.amount -= 1;
+
+ // player should have enough
+ assert!(*for_cost <= target.currency);
+ target.currency -= for_cost;
+
+ target.discard.push(pile.card.clone());
+ }
+ Command::Draw { amount } => {
+ for _ in 0..*amount {
+ if target.deck.is_empty() {
+ shuffle_discard_to_deck(target, action.seed.parse::<u64>().unwrap());
+ }
+
+ target
+ .hand
+ .push(target.deck.pop().unwrap_or_else(|| unreachable!()));
}
- #[allow(unreachable_patterns)]
- _ => todo!(),
}
+ Command::Discard { index } => {
+ // index should be within range
+ assert!(*index <= target.hand.len());
+ target.discard.push(target.hand.remove(*index));
+ }
+ Command::EndTurn {} => {
+ // NOTE: target will be the next player
+
+ // set player to idle
+ invoker.state = PlayerState::Idle;
+
+ // clear currency
+ invoker.currency = 0;
+
+ let start_turn_action = Action::new(
+ &game.id,
+ &action.invoker,
+ &action.target,
+ &Command::StartTurn {},
+ seed_gen!(),
+ );
+ game_status
+ .actions
+ .insert(action_pos + 1, start_turn_action.clone());
+
+ parse_action(&start_turn_action, game, game_status);
+ }
+ Command::StartTurn {} => {
+ // set the target to the play phase
+ target.state = PlayerState::PlayPhase;
+ }
+ Command::ChangePlayerState { state } => {
+ target.state = *state;
+ }
+ #[allow(unreachable_patterns)]
+ _ => todo!(),
}
-
- Ok(game_status)
}
fn shuffle_discard_to_deck(target: &mut PlayerStatus, seed: u64) {