2024-04-09 08:04:10 -04:00
|
|
|
use std::{borrow::Cow, fmt::Display};
|
2024-03-10 20:27:00 -04:00
|
|
|
|
2024-03-13 08:30:41 -04:00
|
|
|
use serde::Deserialize;
|
2024-03-10 20:27:00 -04:00
|
|
|
use yoke::Yokeable;
|
|
|
|
|
|
|
|
use super::player::Position;
|
|
|
|
|
2024-03-10 20:52:44 -04:00
|
|
|
/// A card in a players deck which can apply an effect
|
|
|
|
/// to the game or advance a solution
|
2024-04-09 08:04:10 -04:00
|
|
|
#[derive(Default, Deserialize, Yokeable)]
|
|
|
|
#[serde(default)]
|
2024-03-10 20:27:00 -04:00
|
|
|
pub struct ActionCard<'a> {
|
|
|
|
/// How much influence the card will cost to play
|
|
|
|
influence: i32,
|
2024-04-09 08:04:10 -04:00
|
|
|
/// The effects the card will have on the game once it's played
|
|
|
|
effects: Vec<Effect>,
|
2024-03-10 20:27:00 -04:00
|
|
|
/// The image on the card
|
|
|
|
image: Cow<'a, str>,
|
|
|
|
/// A description of what the card does
|
|
|
|
description: Cow<'a, str>,
|
2024-04-09 08:04:10 -04:00
|
|
|
/// The name of the card
|
|
|
|
name: Cow<'a, str>,
|
|
|
|
/// The position that this card can exclusively be used by
|
|
|
|
position: Option<Position>,
|
|
|
|
/// 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(())
|
|
|
|
}
|
2024-03-10 20:27:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A card which must be solved by someone on the politburo
|
2024-04-09 08:04:10 -04:00
|
|
|
#[derive(Default, Deserialize)]
|
|
|
|
#[serde(default)]
|
2024-03-10 20:27:00 -04:00
|
|
|
pub struct SituationCard<'a> {
|
|
|
|
/// Points associated with the card
|
|
|
|
points: i32,
|
|
|
|
/// Solving this card benefits the player in this position
|
2024-04-09 08:04:10 -04:00
|
|
|
benefactor: Vec<Position>,
|
2024-03-10 20:27:00 -04:00
|
|
|
/// The image on the card
|
|
|
|
image: Cow<'a, str>,
|
|
|
|
/// The name of the card
|
|
|
|
name: Cow<'a, str>,
|
|
|
|
/// A description of the situation the card entails
|
|
|
|
description: Cow<'a, str>,
|
|
|
|
}
|
|
|
|
|
2024-04-09 08:04:10 -04:00
|
|
|
#[derive(Deserialize)]
|
2024-03-10 20:27:00 -04:00
|
|
|
pub enum CardType {
|
|
|
|
/// A card which can be used by anyone
|
|
|
|
General,
|
|
|
|
/// A card which can only be used by the given
|
|
|
|
/// poisition
|
|
|
|
Position(Position),
|
|
|
|
}
|
|
|
|
|
2024-04-09 08:04:10 -04:00
|
|
|
#[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<Position>,
|
|
|
|
},
|
|
|
|
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(()),
|
|
|
|
}
|
|
|
|
}
|
2024-03-13 08:30:41 -04:00
|
|
|
}
|