pub mod cost_payment_stack;
pub mod magic_stack;
pub mod player_zone;
pub mod player_zone_battlefield;
pub mod zone_store;
pub mod zone_type;
use forge_foundation::ZoneType;
use serde::{Deserialize, Serialize};
use crate::ids::{CardId, PlayerId};
pub use cost_payment_stack::CostPaymentStack;
pub use player_zone::PlayerZone;
pub use player_zone_battlefield::PlayerZoneBattlefield;
pub use zone_store::ZoneStore;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Zone {
pub zone_type: ZoneType,
pub owner: PlayerId,
pub cards: Vec<CardId>,
#[serde(skip)]
pub cards_added_this_turn: Vec<(ZoneType, CardId)>,
#[serde(skip)]
pub cards_added_last_turn: Vec<(ZoneType, CardId)>,
#[serde(default)]
pub melded_cards: Vec<CardId>,
#[serde(default)]
pub triggers_enabled: bool,
}
impl Zone {
pub fn new(zone_type: ZoneType, owner: PlayerId) -> Self {
Zone {
zone_type,
owner,
cards: Vec::new(),
cards_added_this_turn: Vec::new(),
cards_added_last_turn: Vec::new(),
melded_cards: Vec::new(),
triggers_enabled: zone_type == ZoneType::Battlefield,
}
}
pub fn add(&mut self, card: CardId) {
self.cards.push(card);
}
pub fn add_to_top(&mut self, card: CardId) {
self.cards.push(card);
}
pub fn add_to_bottom(&mut self, card: CardId) {
self.cards.insert(0, card);
}
pub fn remove(&mut self, card: CardId) -> bool {
if let Some(pos) = self.cards.iter().position(|&c| c == card) {
self.cards.remove(pos);
true
} else {
false
}
}
pub fn remove_all_cards(&mut self) {
self.cards.clear();
}
pub fn reorder(&mut self, card: CardId, index: usize) {
if let Some(pos) = self.cards.iter().position(|&c| c == card) {
self.cards.remove(pos);
let idx = index.min(self.cards.len());
self.cards.insert(idx, card);
}
}
pub fn contains(&self, card: CardId) -> bool {
self.cards.contains(&card)
}
pub fn is(&self, zone_type: ZoneType) -> bool {
self.zone_type == zone_type
}
pub fn size(&self) -> usize {
self.cards.len()
}
pub fn len(&self) -> usize {
self.cards.len()
}
pub fn is_empty(&self) -> bool {
self.cards.is_empty()
}
pub fn get(&self, index: usize) -> Option<CardId> {
self.cards.get(index).copied()
}
pub fn take_top(&mut self) -> Option<CardId> {
self.cards.pop()
}
pub fn peek_top(&self) -> Option<CardId> {
self.cards.last().copied()
}
pub fn reset_cards_added_this_turn(&mut self) {
self.cards_added_last_turn = std::mem::take(&mut self.cards_added_this_turn);
}
pub fn iterator(&self) -> impl Iterator<Item = &CardId> {
self.cards.iter()
}
pub fn shuffle(&mut self, rng: &mut dyn crate::game_rng::GameRng) {
rng.shuffle_cards(&mut self.cards);
}
pub fn add_to_melded(&mut self, card: CardId) {
self.melded_cards.push(card);
}
pub fn remove_from_melded(&mut self, card: CardId) {
if let Some(pos) = self.melded_cards.iter().position(|&c| c == card) {
self.melded_cards.remove(pos);
}
}
pub fn get_cards(&self, _filter: bool) -> &[CardId] {
&self.cards
}
pub fn get_cards_added_this_turn(&self, origin: ZoneType) -> Vec<CardId> {
self.cards_added_this_turn
.iter()
.filter(|(z, _)| *z == origin)
.map(|(_, c)| *c)
.collect()
}
pub fn get_cards_added_last_turn(&self, origin: ZoneType) -> Vec<CardId> {
self.cards_added_last_turn
.iter()
.filter(|(z, _)| *z == origin)
.map(|(_, c)| *c)
.collect()
}
pub fn is_card_added_this_turn(&self, card: CardId, origin: ZoneType) -> bool {
self.cards_added_this_turn
.iter()
.any(|(z, c)| *z == origin && *c == card)
}
pub fn get_lki_copy(&self) -> Zone {
Zone {
zone_type: self.zone_type,
owner: self.owner,
cards: self.cards.clone(),
cards_added_this_turn: Vec::new(),
cards_added_last_turn: Vec::new(),
melded_cards: Vec::new(),
triggers_enabled: self.triggers_enabled,
}
}
pub fn save_lki(&mut self, card: CardId, origin_zone: ZoneType) {
if origin_zone == self.zone_type {
return;
}
self.cards_added_this_turn.push((origin_zone, card));
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ZoneKey {
pub zone_type: ZoneType,
pub owner: PlayerId,
}
impl ZoneKey {
pub fn new(zone_type: ZoneType, owner: PlayerId) -> Self {
ZoneKey { zone_type, owner }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zone_add_remove() {
let mut z = Zone::new(ZoneType::Hand, PlayerId(0));
z.add(CardId(1));
z.add(CardId(2));
assert_eq!(z.size(), 2);
assert!(z.contains(CardId(1)));
z.remove(CardId(1));
assert_eq!(z.size(), 1);
assert!(!z.contains(CardId(1)));
}
#[test]
fn zone_reorder() {
let mut z = Zone::new(ZoneType::Library, PlayerId(0));
z.add(CardId(1));
z.add(CardId(2));
z.add(CardId(3));
z.reorder(CardId(3), 0);
assert_eq!(z.get(0), Some(CardId(3)));
}
#[test]
fn zone_is() {
let z = Zone::new(ZoneType::Graveyard, PlayerId(0));
assert!(z.is(ZoneType::Graveyard));
assert!(!z.is(ZoneType::Hand));
}
#[test]
fn reset_cards_added() {
let mut z = Zone::new(ZoneType::Battlefield, PlayerId(0));
z.save_lki(CardId(1), ZoneType::Hand);
assert_eq!(z.cards_added_this_turn.len(), 1);
z.reset_cards_added_this_turn();
assert!(z.cards_added_this_turn.is_empty());
assert_eq!(z.cards_added_last_turn.len(), 1);
}
}