manabrew_engine/zone/
mod.rs1pub mod cost_payment_stack;
6pub mod magic_stack;
7pub mod player_zone;
8pub mod player_zone_battlefield;
9pub mod zone_store;
10pub mod zone_type;
11
12use forge_foundation::ZoneType;
13use serde::{Deserialize, Serialize};
14
15use crate::ids::{CardId, PlayerId};
16
17pub use cost_payment_stack::CostPaymentStack;
19pub use player_zone::PlayerZone;
20pub use player_zone_battlefield::PlayerZoneBattlefield;
21pub use zone_store::ZoneStore;
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Zone {
30 pub zone_type: ZoneType,
31 pub owner: PlayerId,
32 pub cards: Vec<CardId>,
33 #[serde(skip)]
35 pub cards_added_this_turn: Vec<(ZoneType, CardId)>,
36 #[serde(skip)]
38 pub cards_added_last_turn: Vec<(ZoneType, CardId)>,
39
40 #[serde(default)]
44 pub melded_cards: Vec<CardId>,
45
46 #[serde(default)]
50 pub triggers_enabled: bool,
51}
52
53impl Zone {
54 pub fn new(zone_type: ZoneType, owner: PlayerId) -> Self {
55 Zone {
56 zone_type,
57 owner,
58 cards: Vec::new(),
59 cards_added_this_turn: Vec::new(),
60 cards_added_last_turn: Vec::new(),
61 melded_cards: Vec::new(),
62 triggers_enabled: zone_type == ZoneType::Battlefield,
63 }
64 }
65
66 pub fn add(&mut self, card: CardId) {
67 self.cards.push(card);
68 }
69
70 pub fn add_to_top(&mut self, card: CardId) {
71 self.cards.push(card);
72 }
73
74 pub fn add_to_bottom(&mut self, card: CardId) {
75 self.cards.insert(0, card);
76 }
77
78 pub fn remove(&mut self, card: CardId) -> bool {
79 if let Some(pos) = self.cards.iter().position(|&c| c == card) {
80 self.cards.remove(pos);
81 true
82 } else {
83 false
84 }
85 }
86
87 pub fn remove_all_cards(&mut self) {
90 self.cards.clear();
91 }
92
93 pub fn reorder(&mut self, card: CardId, index: usize) {
96 if let Some(pos) = self.cards.iter().position(|&c| c == card) {
97 self.cards.remove(pos);
98 let idx = index.min(self.cards.len());
99 self.cards.insert(idx, card);
100 }
101 }
102
103 pub fn contains(&self, card: CardId) -> bool {
104 self.cards.contains(&card)
105 }
106
107 pub fn is(&self, zone_type: ZoneType) -> bool {
110 self.zone_type == zone_type
111 }
112
113 pub fn size(&self) -> usize {
116 self.cards.len()
117 }
118
119 pub fn len(&self) -> usize {
120 self.cards.len()
121 }
122
123 pub fn is_empty(&self) -> bool {
124 self.cards.is_empty()
125 }
126
127 pub fn get(&self, index: usize) -> Option<CardId> {
130 self.cards.get(index).copied()
131 }
132
133 pub fn take_top(&mut self) -> Option<CardId> {
135 self.cards.pop()
136 }
137
138 pub fn peek_top(&self) -> Option<CardId> {
140 self.cards.last().copied()
141 }
142
143 pub fn reset_cards_added_this_turn(&mut self) {
146 self.cards_added_last_turn = std::mem::take(&mut self.cards_added_this_turn);
147 }
148
149 pub fn iterator(&self) -> impl Iterator<Item = &CardId> {
152 self.cards.iter()
153 }
154
155 pub fn shuffle(&mut self, rng: &mut dyn crate::game_rng::GameRng) {
161 rng.shuffle_cards(&mut self.cards);
162 }
163
164 pub fn add_to_melded(&mut self, card: CardId) {
169 self.melded_cards.push(card);
170 }
171
172 pub fn remove_from_melded(&mut self, card: CardId) {
175 if let Some(pos) = self.melded_cards.iter().position(|&c| c == card) {
176 self.melded_cards.remove(pos);
177 }
178 }
179
180 pub fn get_cards(&self, _filter: bool) -> &[CardId] {
188 &self.cards
189 }
190
191 pub fn get_cards_added_this_turn(&self, origin: ZoneType) -> Vec<CardId> {
194 self.cards_added_this_turn
195 .iter()
196 .filter(|(z, _)| *z == origin)
197 .map(|(_, c)| *c)
198 .collect()
199 }
200
201 pub fn get_cards_added_last_turn(&self, origin: ZoneType) -> Vec<CardId> {
204 self.cards_added_last_turn
205 .iter()
206 .filter(|(z, _)| *z == origin)
207 .map(|(_, c)| *c)
208 .collect()
209 }
210
211 pub fn is_card_added_this_turn(&self, card: CardId, origin: ZoneType) -> bool {
215 self.cards_added_this_turn
216 .iter()
217 .any(|(z, c)| *z == origin && *c == card)
218 }
219
220 pub fn get_lki_copy(&self) -> Zone {
224 Zone {
225 zone_type: self.zone_type,
226 owner: self.owner,
227 cards: self.cards.clone(),
228 cards_added_this_turn: Vec::new(),
229 cards_added_last_turn: Vec::new(),
230 melded_cards: Vec::new(),
231 triggers_enabled: self.triggers_enabled,
232 }
233 }
234
235 pub fn save_lki(&mut self, card: CardId, origin_zone: ZoneType) {
238 if origin_zone == self.zone_type {
239 return;
240 }
241 self.cards_added_this_turn.push((origin_zone, card));
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247pub struct ZoneKey {
248 pub zone_type: ZoneType,
249 pub owner: PlayerId,
250}
251
252impl ZoneKey {
253 pub fn new(zone_type: ZoneType, owner: PlayerId) -> Self {
254 ZoneKey { zone_type, owner }
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn zone_add_remove() {
264 let mut z = Zone::new(ZoneType::Hand, PlayerId(0));
265 z.add(CardId(1));
266 z.add(CardId(2));
267 assert_eq!(z.size(), 2);
268 assert!(z.contains(CardId(1)));
269 z.remove(CardId(1));
270 assert_eq!(z.size(), 1);
271 assert!(!z.contains(CardId(1)));
272 }
273
274 #[test]
275 fn zone_reorder() {
276 let mut z = Zone::new(ZoneType::Library, PlayerId(0));
277 z.add(CardId(1));
278 z.add(CardId(2));
279 z.add(CardId(3));
280 z.reorder(CardId(3), 0);
281 assert_eq!(z.get(0), Some(CardId(3)));
282 }
283
284 #[test]
285 fn zone_is() {
286 let z = Zone::new(ZoneType::Graveyard, PlayerId(0));
287 assert!(z.is(ZoneType::Graveyard));
288 assert!(!z.is(ZoneType::Hand));
289 }
290
291 #[test]
292 fn reset_cards_added() {
293 let mut z = Zone::new(ZoneType::Battlefield, PlayerId(0));
294 z.save_lki(CardId(1), ZoneType::Hand);
295 assert_eq!(z.cards_added_this_turn.len(), 1);
296 z.reset_cards_added_this_turn();
297 assert!(z.cards_added_this_turn.is_empty());
298 assert_eq!(z.cards_added_last_turn.len(), 1);
299 }
300}