Skip to main content

forge_foundation/
card_split.rs

1use serde::{Deserialize, Serialize};
2
3/// How a split/transform/modal card selects which face to show.
4/// Mirrors Java `CardSplitType.FaceSelectionMethod`.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum FaceSelectionMethod {
7    UseActiveFace,
8    UsePrimaryFace,
9    Combine,
10}
11
12/// The type of split/transform/modal card.
13/// Mirrors Java `CardSplitType`.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
15pub enum CardSplitType {
16    #[default]
17    None,
18    Transform,
19    Meld,
20    Split,
21    Flip,
22    Adventure,
23    Omen,
24    Modal,
25    Specialize,
26}
27
28impl CardSplitType {
29    pub fn aggregation_method(self) -> FaceSelectionMethod {
30        match self {
31            Self::None => FaceSelectionMethod::UsePrimaryFace,
32            Self::Transform => FaceSelectionMethod::UseActiveFace,
33            Self::Meld => FaceSelectionMethod::UseActiveFace,
34            Self::Split => FaceSelectionMethod::Combine,
35            Self::Flip => FaceSelectionMethod::UsePrimaryFace,
36            Self::Adventure => FaceSelectionMethod::UsePrimaryFace,
37            Self::Omen => FaceSelectionMethod::UsePrimaryFace,
38            Self::Modal => FaceSelectionMethod::UseActiveFace,
39            Self::Specialize => FaceSelectionMethod::UseActiveFace,
40        }
41    }
42
43    pub fn changed_state_name(self) -> Option<CardStateName> {
44        match self {
45            Self::None => None,
46            Self::Transform => Some(CardStateName::Backside),
47            Self::Meld => Some(CardStateName::Meld),
48            Self::Split => Some(CardStateName::RightSplit),
49            Self::Flip => Some(CardStateName::Flipped),
50            Self::Adventure => Some(CardStateName::Secondary),
51            Self::Omen => Some(CardStateName::Secondary),
52            Self::Modal => Some(CardStateName::Backside),
53            Self::Specialize => None,
54        }
55    }
56
57    pub fn is_dual_faced(self) -> bool {
58        matches!(self, Self::Transform | Self::Meld | Self::Modal)
59    }
60
61    pub fn from_str_compat(s: &str) -> Option<Self> {
62        match s {
63            "None" => Some(Self::None),
64            "Transform" | "DoubleFaced" => Some(Self::Transform),
65            "Meld" => Some(Self::Meld),
66            "Split" => Some(Self::Split),
67            "Flip" => Some(Self::Flip),
68            "Adventure" => Some(Self::Adventure),
69            "Omen" => Some(Self::Omen),
70            "Modal" => Some(Self::Modal),
71            "Specialize" => Some(Self::Specialize),
72            _ => None,
73        }
74    }
75}
76
77/// Which face/state a card is currently showing.
78/// Mirrors Java `CardStateName`.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
80pub enum CardStateName {
81    #[default]
82    Original,
83    FaceDown,
84    Flipped,
85    Backside,
86    Meld,
87    LeftSplit,
88    RightSplit,
89    Secondary,
90    EmptyRoom,
91    SpecializeW,
92    SpecializeU,
93    SpecializeB,
94    SpecializeR,
95    SpecializeG,
96}
97
98impl CardStateName {
99    pub fn from_str_compat(s: &str) -> Option<Self> {
100        let s = s.trim();
101        if s.eq_ignore_ascii_case("All") {
102            return None;
103        }
104        match s {
105            "Original" => Some(Self::Original),
106            "FaceDown" => Some(Self::FaceDown),
107            "Flipped" | "Flip" => Some(Self::Flipped),
108            "Backside" | "DoubleFaced" => Some(Self::Backside),
109            "Meld" => Some(Self::Meld),
110            "LeftSplit" => Some(Self::LeftSplit),
111            "RightSplit" => Some(Self::RightSplit),
112            "Secondary" => Some(Self::Secondary),
113            "EmptyRoom" => Some(Self::EmptyRoom),
114            "SpecializeW" => Some(Self::SpecializeW),
115            "SpecializeU" => Some(Self::SpecializeU),
116            "SpecializeB" => Some(Self::SpecializeB),
117            "SpecializeR" => Some(Self::SpecializeR),
118            "SpecializeG" => Some(Self::SpecializeG),
119            _ => {
120                // Case-insensitive fallback
121                let lower = s.to_ascii_lowercase();
122                match lower.as_str() {
123                    "original" => Some(Self::Original),
124                    "facedown" => Some(Self::FaceDown),
125                    "flipped" | "flip" => Some(Self::Flipped),
126                    "backside" | "doublefaced" => Some(Self::Backside),
127                    "meld" => Some(Self::Meld),
128                    "leftsplit" => Some(Self::LeftSplit),
129                    "rightsplit" => Some(Self::RightSplit),
130                    "secondary" => Some(Self::Secondary),
131                    "emptyroom" => Some(Self::EmptyRoom),
132                    "specializew" => Some(Self::SpecializeW),
133                    "specializeu" => Some(Self::SpecializeU),
134                    "specializeb" => Some(Self::SpecializeB),
135                    "specializer" => Some(Self::SpecializeR),
136                    "specializeg" => Some(Self::SpecializeG),
137                    _ => None,
138                }
139            }
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn split_type_compat() {
150        assert_eq!(
151            CardSplitType::from_str_compat("DoubleFaced"),
152            Some(CardSplitType::Transform)
153        );
154        assert_eq!(
155            CardSplitType::from_str_compat("Transform"),
156            Some(CardSplitType::Transform)
157        );
158    }
159
160    #[test]
161    fn state_name_compat() {
162        assert_eq!(
163            CardStateName::from_str_compat("Flip"),
164            Some(CardStateName::Flipped)
165        );
166        assert_eq!(
167            CardStateName::from_str_compat("DoubleFaced"),
168            Some(CardStateName::Backside)
169        );
170        assert_eq!(CardStateName::from_str_compat("All"), None);
171    }
172
173    #[test]
174    fn dual_faced() {
175        assert!(CardSplitType::Transform.is_dual_faced());
176        assert!(CardSplitType::Modal.is_dual_faced());
177        assert!(!CardSplitType::Split.is_dual_faced());
178        assert!(!CardSplitType::None.is_dual_faced());
179    }
180}