diff --git a/src/cards.rs b/src/cards.rs index 615a09b..b21206e 100644 --- a/src/cards.rs +++ b/src/cards.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, fmt::Display}; use serde::Deserialize; use yoke::Yokeable; @@ -7,27 +7,51 @@ use super::player::Position; /// A card in a players deck which can apply an effect /// to the game or advance a solution -#[derive(Yokeable, Deserialize)] +#[derive(Default, Deserialize, Yokeable)] +#[serde(default)] pub struct ActionCard<'a> { /// How much influence the card will cost to play influence: i32, - /// The effect the card will have on the game once it's played - effect: Effect, + /// The effects the card will have on the game once it's played + effects: Vec, /// The image on the card image: Cow<'a, str>, - /// The name of the card - name: Cow<'a, str>, /// A description of what the card does description: Cow<'a, str>, + /// The name of the card + name: Cow<'a, str>, + /// The position that this card can exclusively be used by + position: Option, + /// The maximum number of times this card can be drawn + draw_limit: u32, +} + +impl<'a> Display for ActionCard<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for effect in &self.effects { + write!(f, "{} ", effect)?; + } + + if self.draw_limit > 0 { + write!( + f, + "This card can only ever be drawn {} times.", + self.draw_limit + )?; + } + + Ok(()) + } } /// A card which must be solved by someone on the politburo -#[derive(Yokeable, Deserialize)] +#[derive(Default, Deserialize)] +#[serde(default)] pub struct SituationCard<'a> { /// Points associated with the card points: i32, /// Solving this card benefits the player in this position - benefactor: Position, + benefactor: Vec, /// The image on the card image: Cow<'a, str>, /// The name of the card @@ -36,6 +60,7 @@ pub struct SituationCard<'a> { description: Cow<'a, str>, } +#[derive(Deserialize)] pub enum CardType { /// A card which can be used by anyone General, @@ -44,8 +69,129 @@ pub enum CardType { Position(Position), } -#[derive(Deserialize)] -pub struct Effect { - effect: char, - magnitude: f32, +#[derive(Deserialize, Default)] +pub enum Effect { + PublicOpinion { + /// How much public opinion changes + magnitude: i32, + /// If the affector of this card is chosen by it's player + position_choosable: bool, + }, + SolutionTrack { + magnitude: i32, + yours: bool, + }, + DrawCard { + number: i32, + position_specific: bool, + }, + OpposingCards { + number: i32, + position: Option, + }, + InfluenceGain { + magnitude: i32, + }, + #[default] + Custom, +} + +impl Display for Effect { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Effect::PublicOpinion { + magnitude, + position_choosable, + } => { + if *magnitude < 0 { + write!(f, "Decrease ")?; + } else { + write!(f, "Increase ")?; + } + + if *position_choosable { + write!(f, "the public opinion of a player you chose ")?; + } else { + write!(f, "your public opinion ")?; + } + + write!(f, "by {}.", magnitude.abs())?; + + if *position_choosable { + write!( + f, + " This player is chosen after it is determined that this card passes." + ) + } else { + Ok(()) + } + } + Effect::SolutionTrack { magnitude, yours } => { + write!(f, "Move ")?; + + if *magnitude < 0 { + write!(f, "down ")?; + } else { + write!(f, "up ")?; + } + + if *yours { + write!(f, "your solution track ")?; + } else { + write!(f, "a solution track of your choice ")?; + } + + write!(f, "by {}.", magnitude.abs()) + } + Effect::DrawCard { + number, + position_specific, + } => { + if *number < 0 { + write!(f, "Discard ")?; + } else { + write!(f, "Draw ")?; + } + + write!(f, "{} ", number.abs())?; + + if *position_specific { + write!(f, "position specific ")?; + } + + if number.abs() <= 1 { + write!(f, "card.") + } else { + write!(f, "cards.") + } + } + Effect::InfluenceGain { magnitude } => { + if *magnitude < 0 { + write!(f, "Decrease ")?; + } else { + write!(f, "Increase ")?; + } + + write!(f, "your influence gain by {}.", magnitude.abs()) + } + Effect::OpposingCards { number, position } => { + if let Some(pos) = position { + write!(f, "The {} ", pos)?; + } else { + write!(f, "A player of your choice ")?; + } + + write!(f, "must ")?; + + if *number < 0 { + write!(f, "discard ")?; + } else { + write!(f, "draw ")?; + } + + write!(f, "{} cards.", number.abs()) + } + Effect::Custom => Ok(()), + } + } } diff --git a/src/player.rs b/src/player.rs index 7fce689..ac92afa 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use serde::Deserialize; use yoke::Yoke; @@ -27,6 +29,20 @@ pub enum Position { PositionEight, } +impl Display for Position { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MilitaryChief => write!(f, "Cheif of Military"), + Self::PropogandaCheif => write!(f, "Cheif of Propoganda"), + Self::InternalSecurityCheif => write!(f, "Cheif of Internal Security"), + Self::EconomicPlanningCheif => write!(f, "Cheif of Economic Planning"), + Self::PositionFive | Self::PositionSix | Self::PositionSeven | Self::PositionEight => { + write!(f, "Untitled Position") + } + } + } +} + /// A player participating in the Politburo pub struct Player { /// The player's position on the Politburo