Skip to main content

dotzuki_engine/party/
party.rs

1//! Generic ordered party container, storage boxes, and a box store.
2//!
3//! Capacities are always **supplied by the game** (never hardcoded to 6 / 12 /
4//! etc.). The engine only enforces the capacity it was given.
5
6use super::{MonsterInstance, MonsterProvider};
7
8/// Error returned when adding to a full [`Party`] or [`StorageBox`].
9///
10/// Carries the rejected monster back to the caller so nothing is lost.
11#[derive(Debug, PartialEq, Eq)]
12pub struct PartyFull<P: MonsterProvider>(pub MonsterInstance<P>);
13
14/// An ordered party of monsters with a provider/param-defined capacity.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct Party<P: MonsterProvider> {
17    members: Vec<MonsterInstance<P>>,
18    capacity: usize,
19}
20
21impl<P: MonsterProvider> Party<P> {
22    /// Create an empty party with the given capacity (supplied by the game,
23    /// **not** hardcoded).
24    pub fn new(capacity: usize) -> Self {
25        Self {
26            members: Vec::new(),
27            capacity,
28        }
29    }
30
31    /// The capacity this party was created with.
32    pub fn capacity(&self) -> usize {
33        self.capacity
34    }
35
36    /// Number of monsters currently in the party.
37    pub fn len(&self) -> usize {
38        self.members.len()
39    }
40
41    /// Whether the party has no members.
42    pub fn is_empty(&self) -> bool {
43        self.members.is_empty()
44    }
45
46    /// Whether the party is at capacity.
47    pub fn is_full(&self) -> bool {
48        self.members.len() >= self.capacity
49    }
50
51    /// Append a monster. Returns [`PartyFull`] (carrying the monster back) if
52    /// the party is already full.
53    pub fn add(&mut self, m: MonsterInstance<P>) -> Result<(), PartyFull<P>> {
54        if self.is_full() {
55            return Err(PartyFull(m));
56        }
57        self.members.push(m);
58        Ok(())
59    }
60
61    /// Remove and return the monster at `index`, shifting later members down.
62    pub fn remove(&mut self, index: usize) -> Option<MonsterInstance<P>> {
63        if index < self.members.len() {
64            Some(self.members.remove(index))
65        } else {
66            None
67        }
68    }
69
70    /// Swap the monsters at indices `a` and `b`. Out-of-range indices are
71    /// ignored.
72    pub fn swap(&mut self, a: usize, b: usize) {
73        if a < self.members.len() && b < self.members.len() {
74            self.members.swap(a, b);
75        }
76    }
77
78    /// Borrow the monster at `index`.
79    pub fn get(&self, index: usize) -> Option<&MonsterInstance<P>> {
80        self.members.get(index)
81    }
82
83    /// Mutably borrow the monster at `index`.
84    pub fn get_mut(&mut self, index: usize) -> Option<&mut MonsterInstance<P>> {
85        self.members.get_mut(index)
86    }
87
88    /// Index of the first non-fainted member (lead selection), if any.
89    pub fn first_able(&self) -> Option<usize> {
90        self.members.iter().position(|m| !m.is_fainted())
91    }
92
93    /// Whether every member has fainted (battle loss / whiteout). A party with
94    /// no members counts as all-fainted.
95    pub fn all_fainted(&self) -> bool {
96        !self.members.is_empty() && self.members.iter().all(|m| m.is_fainted())
97    }
98
99    /// Iterate over the party members in order.
100    pub fn iter(&self) -> impl Iterator<Item = &MonsterInstance<P>> {
101        self.members.iter()
102    }
103
104    /// Mutably iterate over the party members in order.
105    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut MonsterInstance<P>> {
106        self.members.iter_mut()
107    }
108}
109
110/// A single storage box with a game-defined capacity. Same add/remove/len/
111/// is_full surface as [`Party`].
112#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct StorageBox<P: MonsterProvider> {
114    members: Vec<MonsterInstance<P>>,
115    capacity: usize,
116}
117
118impl<P: MonsterProvider> StorageBox<P> {
119    /// Create an empty box with the given capacity.
120    pub fn new(capacity: usize) -> Self {
121        Self {
122            members: Vec::new(),
123            capacity,
124        }
125    }
126
127    /// The capacity this box was created with.
128    pub fn capacity(&self) -> usize {
129        self.capacity
130    }
131
132    /// Number of monsters stored.
133    pub fn len(&self) -> usize {
134        self.members.len()
135    }
136
137    /// Whether the box is empty.
138    pub fn is_empty(&self) -> bool {
139        self.members.is_empty()
140    }
141
142    /// Whether the box is at capacity.
143    pub fn is_full(&self) -> bool {
144        self.members.len() >= self.capacity
145    }
146
147    /// Store a monster, returning it back via [`PartyFull`] if the box is full.
148    pub fn add(&mut self, m: MonsterInstance<P>) -> Result<(), PartyFull<P>> {
149        if self.is_full() {
150            return Err(PartyFull(m));
151        }
152        self.members.push(m);
153        Ok(())
154    }
155
156    /// Remove and return the monster at `index`.
157    pub fn remove(&mut self, index: usize) -> Option<MonsterInstance<P>> {
158        if index < self.members.len() {
159            Some(self.members.remove(index))
160        } else {
161            None
162        }
163    }
164
165    /// Borrow the monster at `index`.
166    pub fn get(&self, index: usize) -> Option<&MonsterInstance<P>> {
167        self.members.get(index)
168    }
169
170    /// Mutably borrow the monster at `index`.
171    pub fn get_mut(&mut self, index: usize) -> Option<&mut MonsterInstance<P>> {
172        self.members.get_mut(index)
173    }
174
175    /// Iterate over the stored monsters.
176    pub fn iter(&self) -> impl Iterator<Item = &MonsterInstance<P>> {
177        self.members.iter()
178    }
179}
180
181/// A collection of storage boxes plus a "currently selected" box, with both the
182/// box count and per-box capacity supplied by the game.
183#[derive(Clone, Debug, PartialEq, Eq)]
184pub struct BoxStore<P: MonsterProvider> {
185    boxes: Vec<StorageBox<P>>,
186    current: usize,
187}
188
189impl<P: MonsterProvider> BoxStore<P> {
190    /// Create `box_count` boxes, each with `box_capacity` (both supplied).
191    pub fn new(box_count: usize, box_capacity: usize) -> Self {
192        Self {
193            boxes: (0..box_count)
194                .map(|_| StorageBox::new(box_capacity))
195                .collect(),
196            current: 0,
197        }
198    }
199
200    /// Number of boxes.
201    pub fn box_count(&self) -> usize {
202        self.boxes.len()
203    }
204
205    /// Index of the currently selected box.
206    pub fn current_index(&self) -> usize {
207        self.current
208    }
209
210    /// Borrow the currently selected box.
211    pub fn current(&self) -> &StorageBox<P> {
212        &self.boxes[self.current]
213    }
214
215    /// Mutably borrow the currently selected box.
216    pub fn current_mut(&mut self) -> &mut StorageBox<P> {
217        &mut self.boxes[self.current]
218    }
219
220    /// Borrow a specific box by index.
221    pub fn get(&self, index: usize) -> Option<&StorageBox<P>> {
222        self.boxes.get(index)
223    }
224
225    /// Mutably borrow a specific box by index.
226    pub fn get_mut(&mut self, index: usize) -> Option<&mut StorageBox<P>> {
227        self.boxes.get_mut(index)
228    }
229
230    /// Select the box at `index`. Out-of-range indices are ignored.
231    pub fn switch(&mut self, index: usize) {
232        if index < self.boxes.len() {
233            self.current = index;
234        }
235    }
236}