manabrew_engine/zone/player_zone.rs
1//! PlayerZone — a zone owned by a specific player.
2//!
3//! Mirrors Java's `PlayerZone.java`.
4//! Extends Zone with player ownership and activation filtering.
5
6use forge_foundation::ZoneType;
7
8use crate::ids::{CardId, PlayerId};
9
10use super::Zone;
11
12/// A zone owned by a specific player.
13/// Mirrors Java's `PlayerZone` which extends `Zone`.
14///
15/// In Rust, we use composition rather than inheritance:
16/// `PlayerZone` wraps a `Zone` and adds player-specific behavior.
17///
18/// Note: The engine's `GameState` stores zones as `HashMap<ZoneKey, Zone>`.
19/// `PlayerZone` provides the ported Java API for zone-level card filtering;
20/// callers that need card-state filtering (keywords, may-play) should use
21/// `GameState` methods which have access to `CardInstance` data.
22#[derive(Debug, Clone)]
23pub struct PlayerZone {
24 pub zone: Zone,
25 pub player: PlayerId,
26}
27
28impl PlayerZone {
29 pub fn new(zone_type: ZoneType, player: PlayerId) -> Self {
30 PlayerZone {
31 zone: Zone::new(zone_type, player),
32 player,
33 }
34 }
35
36 pub fn get_player(&self) -> PlayerId {
37 self.player
38 }
39
40 /// Test whether a card in this zone passes a containment check.
41 /// Mirrors Java's inner `OwnCardsActivationFilter.test()`.
42 pub fn test(&self, card: CardId) -> bool {
43 self.zone.contains(card)
44 }
45
46 /// Get cards the given player can potentially activate from this zone.
47 /// Mirrors Java's `PlayerZone.getCardsPlayerCanActivate()`.
48 ///
49 /// This performs zone-level filtering:
50 /// - Battlefield/Hand: owner sees all their cards
51 /// - Library: only the top card is visible
52 /// - Other zones: all cards returned (card-level keyword filtering
53 /// like Flashback/Retrace requires `GameState` access)
54 pub fn get_cards_player_can_activate(&self, who: PlayerId) -> Vec<CardId> {
55 let is_owner = who == self.player;
56 let zone_type = self.zone.zone_type;
57
58 // Battlefield and Hand: owner can activate everything
59 if is_owner && (zone_type == ZoneType::Battlefield || zone_type == ZoneType::Hand) {
60 return self.zone.cards.clone();
61 }
62
63 // Library: only the top card is accessible
64 if zone_type == ZoneType::Library {
65 return self.zone.cards.last().copied().into_iter().collect();
66 }
67
68 // Graveyard/Exile/Command: return all (card-level filtering needs GameState)
69 self.zone.cards.clone()
70 }
71}
72
73impl std::ops::Deref for PlayerZone {
74 type Target = Zone;
75 fn deref(&self) -> &Zone {
76 &self.zone
77 }
78}
79
80impl std::ops::DerefMut for PlayerZone {
81 fn deref_mut(&mut self) -> &mut Zone {
82 &mut self.zone
83 }
84}